aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ata/ahci.c8
-rw-r--r--drivers/ata/ata_generic.c5
-rw-r--r--drivers/ata/ata_piix.c21
-rw-r--r--drivers/ata/libata-acpi.c19
-rw-r--r--drivers/ata/libata-core.c204
-rw-r--r--drivers/ata/libata-eh.c116
-rw-r--r--drivers/ata/libata-pmp.c22
-rw-r--r--drivers/ata/libata-scsi.c26
-rw-r--r--drivers/ata/pata_bf54x.c1
-rw-r--r--drivers/ata/pata_it821x.c34
-rw-r--r--drivers/ata/pata_ixp4xx_cf.c14
-rw-r--r--drivers/ata/pata_legacy.c18
-rw-r--r--drivers/ata/pata_oldpiix.c1
-rw-r--r--drivers/ata/pata_pdc2027x.c29
-rw-r--r--drivers/ata/pata_platform.c14
-rw-r--r--drivers/ata/pata_radisys.c1
-rw-r--r--drivers/ata/pata_rz1000.c16
-rw-r--r--drivers/ata/pata_scc.c1
-rw-r--r--drivers/ata/pata_serverworks.c1
-rw-r--r--drivers/ata/pata_sis.c1
-rw-r--r--drivers/ata/sata_mv.c1
-rw-r--r--drivers/ata/sata_sil.c98
-rw-r--r--drivers/block/cciss.c88
-rw-r--r--drivers/block/cciss.h4
-rw-r--r--drivers/block/cciss_cmd.h3
-rw-r--r--drivers/block/loop.c39
-rw-r--r--drivers/block/nbd.c10
-rw-r--r--drivers/block/virtio_blk.c43
-rw-r--r--drivers/block/xen-blkfront.c8
-rw-r--r--drivers/cdrom/cdrom.c703
-rw-r--r--drivers/char/agp/intel-agp.c11
-rw-r--r--drivers/char/ds1620.c25
-rw-r--r--drivers/char/hpet.c2
-rw-r--r--drivers/char/hvc_console.c1
-rw-r--r--drivers/char/nwflash.c8
-rw-r--r--drivers/char/random.c22
-rw-r--r--drivers/char/virtio_console.c30
-rw-r--r--drivers/clocksource/acpi_pm.c10
-rw-r--r--drivers/firmware/dmi_scan.c16
-rw-r--r--drivers/gpu/drm/Kconfig17
-rw-r--r--drivers/gpu/drm/Makefile3
-rw-r--r--drivers/gpu/drm/drm_auth.c29
-rw-r--r--drivers/gpu/drm/drm_bufs.c27
-rw-r--r--drivers/gpu/drm/drm_context.c10
-rw-r--r--drivers/gpu/drm/drm_crtc.c2446
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c826
-rw-r--r--drivers/gpu/drm/drm_drv.c95
-rw-r--r--drivers/gpu/drm/drm_edid.c732
-rw-r--r--drivers/gpu/drm/drm_fops.c223
-rw-r--r--drivers/gpu/drm/drm_gem.c111
-rw-r--r--drivers/gpu/drm/drm_hashtab.c2
-rw-r--r--drivers/gpu/drm/drm_ioctl.c61
-rw-r--r--drivers/gpu/drm/drm_irq.c73
-rw-r--r--drivers/gpu/drm/drm_lock.c42
-rw-r--r--drivers/gpu/drm/drm_mm.c1
-rw-r--r--drivers/gpu/drm/drm_modes.c576
-rw-r--r--drivers/gpu/drm/drm_proc.c71
-rw-r--r--drivers/gpu/drm/drm_stub.c142
-rw-r--r--drivers/gpu/drm/drm_sysfs.c329
-rw-r--r--drivers/gpu/drm/drm_vm.c7
-rw-r--r--drivers/gpu/drm/i915/Makefile17
-rw-r--r--drivers/gpu/drm/i915/dvo.h157
-rw-r--r--drivers/gpu/drm/i915/dvo_ch7017.c454
-rw-r--r--drivers/gpu/drm/i915/dvo_ch7xxx.c368
-rw-r--r--drivers/gpu/drm/i915/dvo_ivch.c442
-rw-r--r--drivers/gpu/drm/i915/dvo_sil164.c302
-rw-r--r--drivers/gpu/drm/i915/dvo_tfp410.c335
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c338
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c42
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h93
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c628
-rw-r--r--drivers/gpu/drm/i915/i915_gem_proc.c34
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c1
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c51
-rw-r--r--drivers/gpu/drm/i915/i915_mem.c3
-rw-r--r--drivers/gpu/drm/i915/i915_opregion.c4
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h20
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c193
-rw-r--r--drivers/gpu/drm/i915/intel_bios.h405
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c284
-rw-r--r--drivers/gpu/drm/i915/intel_display.c1618
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h146
-rw-r--r--drivers/gpu/drm/i915/intel_dvo.c495
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c925
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c184
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c525
-rw-r--r--drivers/gpu/drm/i915/intel_modes.c83
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c1128
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo_regs.h327
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c1725
-rw-r--r--drivers/gpu/drm/radeon/r300_cmdbuf.c11
-rw-r--r--drivers/gpu/drm/radeon/radeon_cp.c73
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.h14
-rw-r--r--drivers/gpu/drm/radeon/radeon_state.c166
-rw-r--r--drivers/i2c/busses/i2c-pxa.c2
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c4
-rw-r--r--drivers/ide/Kconfig10
-rw-r--r--drivers/ide/Makefile3
-rw-r--r--drivers/ide/cmd64x.c4
-rw-r--r--drivers/ide/cy82c693.c1
-rw-r--r--drivers/ide/gayle.c6
-rw-r--r--drivers/ide/hpt366.c8
-rw-r--r--drivers/ide/ide-acpi.c4
-rw-r--r--drivers/ide/ide-cd.c142
-rw-r--r--drivers/ide/ide-cd.h2
-rw-r--r--drivers/ide/ide-dma-sff.c54
-rw-r--r--drivers/ide/ide-io.c292
-rw-r--r--drivers/ide/ide-ioctls.c5
-rw-r--r--drivers/ide/ide-iops.c48
-rw-r--r--drivers/ide/ide-legacy.c58
-rw-r--r--drivers/ide/ide-lib.c105
-rw-r--r--drivers/ide/ide-park.c16
-rw-r--r--drivers/ide/ide-pm.c235
-rw-r--r--drivers/ide/ide-probe.c166
-rw-r--r--drivers/ide/ide-proc.c29
-rw-r--r--drivers/ide/ide.c89
-rw-r--r--drivers/ide/ide_arm.c11
-rw-r--r--drivers/ide/pdc202xx_old.c9
-rw-r--r--drivers/ide/rz1000.c36
-rw-r--r--drivers/ide/trm290.c4
-rw-r--r--drivers/ide/tx4938ide.c4
-rw-r--r--drivers/ide/tx4939ide.c10
-rw-r--r--drivers/ide/umc8672.c11
-rw-r--r--drivers/infiniband/Kconfig1
-rw-r--r--drivers/infiniband/core/addr.c47
-rw-r--r--drivers/infiniband/hw/mlx4/cq.c2
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c48
-rw-r--r--drivers/infiniband/ulp/iser/iser_initiator.c5
-rw-r--r--drivers/input/keyboard/omap-keypad.c8
-rw-r--r--drivers/input/keyboard/pxa27x_keypad.c2
-rw-r--r--drivers/input/serio/Kconfig2
-rw-r--r--drivers/input/touchscreen/ads7846.c4
-rw-r--r--drivers/input/touchscreen/mainstone-wm97xx.c2
-rw-r--r--drivers/lguest/lg.h2
-rw-r--r--drivers/lguest/lguest_device.c8
-rw-r--r--drivers/lguest/lguest_user.c13
-rw-r--r--drivers/lguest/page_tables.c72
-rw-r--r--drivers/md/dm-crypt.c2
-rw-r--r--drivers/md/dm-io.c2
-rw-r--r--drivers/md/dm.c2
-rw-r--r--drivers/media/common/ir-keymaps.c93
-rw-r--r--drivers/media/common/saa7146_fops.c2
-rw-r--r--drivers/media/common/saa7146_video.c10
-rw-r--r--drivers/media/common/tuners/mxl5005s.c6
-rw-r--r--drivers/media/common/tuners/tda827x.c15
-rw-r--r--drivers/media/common/tuners/tda8290.c63
-rw-r--r--drivers/media/common/tuners/tda9887.c5
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c35
-rw-r--r--drivers/media/common/tuners/xc5000.c7
-rw-r--r--drivers/media/dvb/Kconfig13
-rw-r--r--drivers/media/dvb/b2c2/Kconfig1
-rw-r--r--drivers/media/dvb/dm1105/dm1105.c3
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c77
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h134
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.c71
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.h1
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.c33
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.h140
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.c2
-rw-r--r--drivers/media/dvb/dvb-usb/cinergyT2-core.c3
-rw-r--r--drivers/media/dvb/dvb-usb/cinergyT2.h10
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h1
-rw-r--r--drivers/media/dvb/dvb-usb/dw2102.c15
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk-fe.c140
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.c16
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.h1
-rw-r--r--drivers/media/dvb/dvb-usb/usb-urb.c3
-rw-r--r--drivers/media/dvb/frontends/Kconfig53
-rw-r--r--drivers/media/dvb/frontends/Makefile10
-rw-r--r--drivers/media/dvb/frontends/af9013.c14
-rw-r--r--drivers/media/dvb/frontends/cx24113.c616
-rw-r--r--drivers/media/dvb/frontends/cx24113.h11
-rw-r--r--drivers/media/dvb/frontends/cx24116.c43
-rw-r--r--drivers/media/dvb/frontends/dib7000p.h9
-rw-r--r--drivers/media/dvb/frontends/drx397xD.c12
-rw-r--r--drivers/media/dvb/frontends/drx397xD_fw.h4
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c2
-rw-r--r--drivers/media/dvb/frontends/lgdt3304.c378
-rw-r--r--drivers/media/dvb/frontends/lgdt3304.h45
-rw-r--r--drivers/media/dvb/frontends/s5h1411.c3
-rw-r--r--drivers/media/dvb/frontends/s921_core.c216
-rw-r--r--drivers/media/dvb/frontends/s921_core.h114
-rw-r--r--drivers/media/dvb/frontends/s921_module.c190
-rw-r--r--drivers/media/dvb/frontends/s921_module.h49
-rw-r--r--drivers/media/dvb/frontends/si21xx.c1
-rw-r--r--drivers/media/dvb/frontends/stb0899_algo.c1519
-rw-r--r--drivers/media/dvb/frontends/stb0899_cfg.h287
-rw-r--r--drivers/media/dvb/frontends/stb0899_drv.c1684
-rw-r--r--drivers/media/dvb/frontends/stb0899_drv.h162
-rw-r--r--drivers/media/dvb/frontends/stb0899_priv.h267
-rw-r--r--drivers/media/dvb/frontends/stb0899_reg.h2027
-rw-r--r--drivers/media/dvb/frontends/stb6100.c545
-rw-r--r--drivers/media/dvb/frontends/stb6100.h115
-rw-r--r--drivers/media/dvb/frontends/stb6100_cfg.h108
-rw-r--r--drivers/media/dvb/frontends/tda8261.c230
-rw-r--r--drivers/media/dvb/frontends/tda8261.h55
-rw-r--r--drivers/media/dvb/frontends/tda8261_cfg.h84
-rw-r--r--drivers/media/dvb/frontends/zl10353.c3
-rw-r--r--drivers/media/dvb/siano/sms-cards.c110
-rw-r--r--drivers/media/dvb/siano/sms-cards.h13
-rw-r--r--drivers/media/dvb/siano/smscoreapi.c78
-rw-r--r--drivers/media/dvb/siano/smscoreapi.h36
-rw-r--r--drivers/media/dvb/siano/smsdvb.c56
-rw-r--r--drivers/media/dvb/siano/smsusb.c45
-rw-r--r--drivers/media/dvb/ttpci/Kconfig4
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c298
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c311
-rw-r--r--drivers/media/dvb/ttpci/budget.h1
-rw-r--r--drivers/media/radio/dsbr100.c381
-rw-r--r--drivers/media/radio/radio-aimslab.c2
-rw-r--r--drivers/media/radio/radio-cadet.c2
-rw-r--r--drivers/media/radio/radio-gemtek.c2
-rw-r--r--drivers/media/radio/radio-mr800.c123
-rw-r--r--drivers/media/radio/radio-rtrack2.c2
-rw-r--r--drivers/media/radio/radio-sf16fmi.c2
-rw-r--r--drivers/media/video/Kconfig48
-rw-r--r--drivers/media/video/Makefile12
-rw-r--r--drivers/media/video/arv.c5
-rw-r--r--drivers/media/video/bt8xx/bt832.c274
-rw-r--r--drivers/media/video/bt8xx/bt832.h305
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c72
-rw-r--r--drivers/media/video/bt8xx/bttv-gpio.c7
-rw-r--r--drivers/media/video/bt8xx/bttv.h10
-rw-r--r--drivers/media/video/bt8xx/bttvp.h2
-rw-r--r--drivers/media/video/bw-qcam.c5
-rw-r--r--drivers/media/video/c-qcam.c7
-rw-r--r--drivers/media/video/cpia.c9
-rw-r--r--drivers/media/video/cpia2/cpia2_core.c2
-rw-r--r--drivers/media/video/cpia2/cpia2_usb.c2
-rw-r--r--drivers/media/video/cpia2/cpia2_v4l.c15
-rw-r--r--drivers/media/video/cs5345.c227
-rw-r--r--drivers/media/video/cs53l32a.c188
-rw-r--r--drivers/media/video/cx18/cx18-av-audio.c231
-rw-r--r--drivers/media/video/cx18/cx18-av-core.c106
-rw-r--r--drivers/media/video/cx18/cx18-av-core.h5
-rw-r--r--drivers/media/video/cx18/cx18-av-firmware.c28
-rw-r--r--drivers/media/video/cx18/cx18-av-vbi.c5
-rw-r--r--drivers/media/video/cx18/cx18-cards.c9
-rw-r--r--drivers/media/video/cx18/cx18-cards.h6
-rw-r--r--drivers/media/video/cx18/cx18-controls.c5
-rw-r--r--drivers/media/video/cx18/cx18-driver.c284
-rw-r--r--drivers/media/video/cx18/cx18-driver.h78
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c59
-rw-r--r--drivers/media/video/cx18/cx18-dvb.h1
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c38
-rw-r--r--drivers/media/video/cx18/cx18-firmware.c229
-rw-r--r--drivers/media/video/cx18/cx18-gpio.c23
-rw-r--r--drivers/media/video/cx18/cx18-gpio.h1
-rw-r--r--drivers/media/video/cx18/cx18-i2c.c31
-rw-r--r--drivers/media/video/cx18/cx18-io.c198
-rw-r--r--drivers/media/video/cx18/cx18-io.h326
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c12
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.h1
-rw-r--r--drivers/media/video/cx18/cx18-irq.c163
-rw-r--r--drivers/media/video/cx18/cx18-irq.h4
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c527
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.h29
-rw-r--r--drivers/media/video/cx18/cx18-queue.c118
-rw-r--r--drivers/media/video/cx18/cx18-queue.h22
-rw-r--r--drivers/media/video/cx18/cx18-scb.c2
-rw-r--r--drivers/media/video/cx18/cx18-scb.h9
-rw-r--r--drivers/media/video/cx18/cx18-streams.c140
-rw-r--r--drivers/media/video/cx18/cx18-streams.h5
-rw-r--r--drivers/media/video/cx18/cx18-vbi.c5
-rw-r--r--drivers/media/video/cx18/cx18-version.h2
-rw-r--r--drivers/media/video/cx18/cx23418.h6
-rw-r--r--drivers/media/video/cx23885/cx23885-417.c2
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c12
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c1
-rw-r--r--drivers/media/video/cx23885/cx23885.h1
-rw-r--r--drivers/media/video/cx25840/Kconfig2
-rw-r--r--drivers/media/video/cx25840/cx25840-audio.c14
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c447
-rw-r--r--drivers/media/video/cx25840/cx25840-core.h7
-rw-r--r--drivers/media/video/cx25840/cx25840-firmware.c2
-rw-r--r--drivers/media/video/cx25840/cx25840-vbi.c2
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c3
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c16
-rw-r--r--drivers/media/video/cx88/cx88-cards.c86
-rw-r--r--drivers/media/video/cx88/cx88-core.c3
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c230
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c4
-rw-r--r--drivers/media/video/cx88/cx88.h14
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c21
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c1464
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c628
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c14
-rw-r--r--drivers/media/video/em28xx/em28xx-i2c.c49
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c311
-rw-r--r--drivers/media/video/em28xx/em28xx-reg.h153
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c851
-rw-r--r--drivers/media/video/em28xx/em28xx.h148
-rw-r--r--drivers/media/video/et61x251/et61x251_core.c2
-rw-r--r--drivers/media/video/gspca/Kconfig23
-rw-r--r--drivers/media/video/gspca/Makefile4
-rw-r--r--drivers/media/video/gspca/conex.c2
-rw-r--r--drivers/media/video/gspca/etoms.c4
-rw-r--r--drivers/media/video/gspca/finepix.c5
-rw-r--r--drivers/media/video/gspca/gspca.c210
-rw-r--r--drivers/media/video/gspca/gspca.h25
-rw-r--r--drivers/media/video/gspca/m5602/m5602_bridge.h119
-rw-r--r--drivers/media/video/gspca/m5602/m5602_core.c100
-rw-r--r--drivers/media/video/gspca/m5602/m5602_mt9m111.c135
-rw-r--r--drivers/media/video/gspca/m5602/m5602_mt9m111.h14
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov9650.c316
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov9650.h195
-rw-r--r--drivers/media/video/gspca/m5602/m5602_po1030.c166
-rw-r--r--drivers/media/video/gspca/m5602/m5602_po1030.h10
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.c235
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.h47
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k83a.c213
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k83a.h25
-rw-r--r--drivers/media/video/gspca/m5602/m5602_sensor.h14
-rw-r--r--drivers/media/video/gspca/mars.c4
-rw-r--r--drivers/media/video/gspca/ov519.c172
-rw-r--r--drivers/media/video/gspca/ov534.c601
-rw-r--r--drivers/media/video/gspca/pac207.c8
-rw-r--r--drivers/media/video/gspca/pac7311.c5
-rw-r--r--drivers/media/video/gspca/sonixb.c25
-rw-r--r--drivers/media/video/gspca/sonixj.c508
-rw-r--r--drivers/media/video/gspca/spca500.c8
-rw-r--r--drivers/media/video/gspca/spca501.c148
-rw-r--r--drivers/media/video/gspca/spca505.c2
-rw-r--r--drivers/media/video/gspca/spca506.c2
-rw-r--r--drivers/media/video/gspca/spca508.c2
-rw-r--r--drivers/media/video/gspca/spca561.c522
-rw-r--r--drivers/media/video/gspca/stk014.c8
-rw-r--r--drivers/media/video/gspca/stv06xx/Kconfig9
-rw-r--r--drivers/media/video/gspca/stv06xx/Makefile9
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.c522
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.h107
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c535
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h263
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c430
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h275
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_sensor.h92
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c251
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h315
-rw-r--r--drivers/media/video/gspca/sunplus.c6
-rw-r--r--drivers/media/video/gspca/t613.c4
-rw-r--r--drivers/media/video/gspca/tv8532.c142
-rw-r--r--drivers/media/video/gspca/vc032x.c819
-rw-r--r--drivers/media/video/gspca/zc3xx-reg.h8
-rw-r--r--drivers/media/video/gspca/zc3xx.c1012
-rw-r--r--drivers/media/video/ir-kbd-i2c.c6
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.c16
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.c16
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c214
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h52
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.c44
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.c324
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.h3
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.c314
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.h13
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c73
-rw-r--r--drivers/media/video/ivtv/ivtv-routing.c12
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c15
-rw-r--r--drivers/media/video/ivtv/ivtv-vbi.c17
-rw-r--r--drivers/media/video/ivtv/ivtvfb.c91
-rw-r--r--drivers/media/video/m52790.c176
-rw-r--r--drivers/media/video/msp3400-driver.c402
-rw-r--r--drivers/media/video/msp3400-driver.h7
-rw-r--r--drivers/media/video/msp3400-kthreads.c34
-rw-r--r--drivers/media/video/mt9m001.c60
-rw-r--r--drivers/media/video/mt9m111.c121
-rw-r--r--drivers/media/video/mt9t031.c736
-rw-r--r--drivers/media/video/mt9v022.c46
-rw-r--r--drivers/media/video/omap24xxcam-dma.c601
-rw-r--r--drivers/media/video/omap24xxcam.c1908
-rw-r--r--drivers/media/video/omap24xxcam.h593
-rw-r--r--drivers/media/video/ov511.c5
-rw-r--r--drivers/media/video/ov772x.c1012
-rw-r--r--drivers/media/video/pms.c9
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.c4
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c13
-rw-r--r--drivers/media/video/pwc/pwc-if.c2
-rw-r--r--drivers/media/video/pwc/pwc-v4l.c3
-rw-r--r--drivers/media/video/pwc/pwc.h3
-rw-r--r--drivers/media/video/pxa_camera.c545
-rw-r--r--drivers/media/video/pxa_camera.h95
-rw-r--r--drivers/media/video/saa5246a.c7
-rw-r--r--drivers/media/video/saa5249.c7
-rw-r--r--drivers/media/video/saa7115.c763
-rw-r--r--drivers/media/video/saa7127.c421
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c52
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c24
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c14
-rw-r--r--drivers/media/video/saa7134/saa7134-tvaudio.c2
-rw-r--r--drivers/media/video/saa7134/saa7134.h3
-rw-r--r--drivers/media/video/saa717x.c610
-rw-r--r--drivers/media/video/se401.c5
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c309
-rw-r--r--drivers/media/video/sn9c102/sn9c102_core.c2
-rw-r--r--drivers/media/video/sn9c102/sn9c102_devtable.h8
-rw-r--r--drivers/media/video/soc_camera.c389
-rw-r--r--drivers/media/video/soc_camera_platform.c17
-rw-r--r--drivers/media/video/stk-webcam.c27
-rw-r--r--drivers/media/video/stv680.c5
-rw-r--r--drivers/media/video/tda7432.c252
-rw-r--r--drivers/media/video/tda9840.c188
-rw-r--r--drivers/media/video/tda9875.c348
-rw-r--r--drivers/media/video/tea6415c.c49
-rw-r--r--drivers/media/video/tea6420.c49
-rw-r--r--drivers/media/video/tlv320aic23b.c141
-rw-r--r--drivers/media/video/tuner-core.c397
-rw-r--r--drivers/media/video/tvaudio.c707
-rw-r--r--drivers/media/video/tvp514x.c1569
-rw-r--r--drivers/media/video/tvp514x_regs.h297
-rw-r--r--drivers/media/video/tvp5150.c827
-rw-r--r--drivers/media/video/tw9910.c951
-rw-r--r--drivers/media/video/upd64031a.c193
-rw-r--r--drivers/media/video/upd64083.c166
-rw-r--r--drivers/media/video/usbvideo/ibmcam.c4
-rw-r--r--drivers/media/video/usbvideo/konicawc.c4
-rw-r--r--drivers/media/video/usbvideo/quickcam_messenger.c9
-rw-r--r--drivers/media/video/usbvideo/ultracam.c4
-rw-r--r--drivers/media/video/usbvideo/usbvideo.c7
-rw-r--r--drivers/media/video/usbvideo/vicam.c3
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c11
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c143
-rw-r--r--drivers/media/video/uvc/uvc_driver.c332
-rw-r--r--drivers/media/video/uvc/uvc_queue.c23
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c115
-rw-r--r--drivers/media/video/uvc/uvc_video.c214
-rw-r--r--drivers/media/video/uvc/uvcvideo.h34
-rw-r--r--drivers/media/video/v4l2-common.c203
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c (renamed from drivers/media/video/compat_ioctl32.c)835
-rw-r--r--drivers/media/video/v4l2-dev.c365
-rw-r--r--drivers/media/video/v4l2-device.c86
-rw-r--r--drivers/media/video/v4l2-ioctl.c98
-rw-r--r--drivers/media/video/v4l2-subdev.c110
-rw-r--r--drivers/media/video/vino.c5
-rw-r--r--drivers/media/video/vp27smpx.c126
-rw-r--r--drivers/media/video/w9966.c5
-rw-r--r--drivers/media/video/wm8739.c188
-rw-r--r--drivers/media/video/wm8775.c221
-rw-r--r--drivers/media/video/zc0301/zc0301_core.c2
-rw-r--r--drivers/media/video/zoran/zoran_card.c6
-rw-r--r--drivers/media/video/zoran/zoran_driver.c8
-rw-r--r--drivers/message/fusion/mptbase.c15
-rw-r--r--drivers/mfd/asic3.c6
-rw-r--r--drivers/mfd/mcp-core.c2
-rw-r--r--drivers/mfd/mcp-sa11x0.c2
-rw-r--r--drivers/mfd/ucb1x00-assabet.c2
-rw-r--r--drivers/mfd/ucb1x00-core.c2
-rw-r--r--drivers/mfd/ucb1x00-ts.c2
-rw-r--r--drivers/mmc/host/imxmmc.c459
-rw-r--r--drivers/mmc/host/imxmmc.h37
-rw-r--r--drivers/mmc/host/mmci.c2
-rw-r--r--drivers/mmc/host/omap.c7
-rw-r--r--drivers/mmc/host/pxamci.c7
-rw-r--r--drivers/mmc/host/s3cmci.c2
-rw-r--r--drivers/mtd/maps/dc21285.c7
-rw-r--r--drivers/mtd/maps/ixp2000.c2
-rw-r--r--drivers/mtd/maps/ixp4xx.c2
-rw-r--r--drivers/mtd/nand/Kconfig2
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c4
-rw-r--r--drivers/mtd/nand/s3c2410.c8
-rw-r--r--drivers/mtd/onenand/omap2.c10
-rw-r--r--drivers/net/Kconfig7
-rw-r--r--drivers/net/arm/ep93xx_eth.c2
-rw-r--r--drivers/net/arm/ixp4xx_eth.c2
-rw-r--r--drivers/net/arm/ks8695net.c1
-rw-r--r--drivers/net/cs89x0.c6
-rw-r--r--drivers/net/eexpress.h56
-rw-r--r--drivers/net/irda/pxaficp_ir.c46
-rw-r--r--drivers/net/irda/sa1100_ir.c2
-rw-r--r--drivers/net/mlx4/en_main.c11
-rw-r--r--drivers/net/mlx4/en_netdev.c8
-rw-r--r--drivers/net/mlx4/en_params.c87
-rw-r--r--drivers/net/mlx4/mlx4_en.h6
-rw-r--r--drivers/net/pasemi_mac.c2
-rw-r--r--drivers/net/smc911x.h3
-rw-r--r--drivers/net/smc91x.h3
-rw-r--r--drivers/net/smsc911x.c4
-rw-r--r--drivers/net/spider_net.c1
-rw-r--r--drivers/net/tun.c2
-rw-r--r--drivers/net/usb/hso.c2
-rw-r--r--drivers/net/wan/ixp4xx_hss.c2
-rw-r--r--drivers/net/wireless/zd1211rw/zd_usb.c3
-rw-r--r--drivers/oprofile/buffer_sync.c117
-rw-r--r--drivers/oprofile/cpu_buffer.c197
-rw-r--r--drivers/oprofile/cpu_buffer.h69
-rw-r--r--drivers/oprofile/oprofile_files.c15
-rw-r--r--drivers/pci/intr_remapping.c77
-rw-r--r--drivers/pci/msi.c55
-rw-r--r--drivers/pcmcia/Kconfig2
-rw-r--r--drivers/pcmcia/Makefile1
-rw-r--r--drivers/pcmcia/pxa2xx_e740.c176
-rw-r--r--drivers/rtc/rtc-at91sam9.c1
-rw-r--r--drivers/rtc/rtc-s3c.c2
-rw-r--r--drivers/rtc/rtc-sa1100.c12
-rw-r--r--drivers/s390/kvm/kvm_virtio.c34
-rw-r--r--drivers/s390/scsi/zfcp_aux.c110
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c6
-rw-r--r--drivers/s390/scsi/zfcp_cfdc.c17
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c12
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h1
-rw-r--r--drivers/s390/scsi/zfcp_def.h32
-rw-r--r--drivers/s390/scsi/zfcp_erp.c10
-rw-r--r--drivers/s390/scsi/zfcp_fc.c88
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c77
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h2
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c4
-rw-r--r--drivers/scsi/Kconfig22
-rw-r--r--drivers/scsi/Makefile5
-rw-r--r--drivers/scsi/NCR5380.c2
-rw-r--r--drivers/scsi/a100u2w.c2
-rw-r--r--drivers/scsi/aacraid/aachba.c2
-rw-r--r--drivers/scsi/aacraid/commctrl.c14
-rw-r--r--drivers/scsi/aacraid/comminit.c2
-rw-r--r--drivers/scsi/aacraid/commsup.c2
-rw-r--r--drivers/scsi/aacraid/dpcsup.c2
-rw-r--r--drivers/scsi/aacraid/linit.c2
-rw-r--r--drivers/scsi/aacraid/rkt.c2
-rw-r--r--drivers/scsi/aacraid/rx.c2
-rw-r--r--drivers/scsi/aacraid/sa.c2
-rw-r--r--drivers/scsi/advansys.c3
-rw-r--r--drivers/scsi/aha1740.c2
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c8
-rw-r--r--drivers/scsi/atp870u.c4
-rw-r--r--drivers/scsi/ch.c2
-rw-r--r--drivers/scsi/cxgb3i/Kbuild4
-rw-r--r--drivers/scsi/cxgb3i/Kconfig7
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i.h139
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_ddp.c770
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_ddp.h306
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_init.c107
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_iscsi.c951
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_offload.c1810
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_offload.h231
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_pdu.c402
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_pdu.h59
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c18
-rw-r--r--drivers/scsi/eata.c15
-rw-r--r--drivers/scsi/eata_pio.c4
-rw-r--r--drivers/scsi/esp_scsi.c6
-rw-r--r--drivers/scsi/fcoe/Makefile8
-rw-r--r--drivers/scsi/fcoe/fc_transport_fcoe.c446
-rw-r--r--drivers/scsi/fcoe/fcoe_sw.c494
-rw-r--r--drivers/scsi/fcoe/libfcoe.c1510
-rw-r--r--drivers/scsi/fdomain.c2
-rw-r--r--drivers/scsi/ibmmca.c2
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c293
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h32
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c4
-rw-r--r--drivers/scsi/ide-scsi.c32
-rw-r--r--drivers/scsi/in2000.c2
-rw-r--r--drivers/scsi/initio.c2
-rw-r--r--drivers/scsi/initio.h2
-rw-r--r--drivers/scsi/ipr.c6
-rw-r--r--drivers/scsi/ipr.h2
-rw-r--r--drivers/scsi/iscsi_tcp.c1621
-rw-r--r--drivers/scsi/iscsi_tcp.h88
-rw-r--r--drivers/scsi/libfc/Makefile12
-rw-r--r--drivers/scsi/libfc/fc_disc.c845
-rw-r--r--drivers/scsi/libfc/fc_elsct.c71
-rw-r--r--drivers/scsi/libfc/fc_exch.c1970
-rw-r--r--drivers/scsi/libfc/fc_fcp.c2131
-rw-r--r--drivers/scsi/libfc/fc_frame.c89
-rw-r--r--drivers/scsi/libfc/fc_lport.c1604
-rw-r--r--drivers/scsi/libfc/fc_rport.c1291
-rw-r--r--drivers/scsi/libiscsi.c236
-rw-r--r--drivers/scsi/libiscsi_tcp.c1163
-rw-r--r--drivers/scsi/lpfc/lpfc.h22
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c169
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h15
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c17
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c164
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c160
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c69
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h249
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c685
-rw-r--r--drivers/scsi/lpfc/lpfc_logmsg.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c54
-rw-r--r--drivers/scsi/lpfc/lpfc_nl.h30
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c2
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c1235
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c159
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c28
-rw-r--r--drivers/scsi/mac_esp.c100
-rw-r--r--drivers/scsi/nsp32.c3
-rw-r--r--drivers/scsi/qla1280.c3
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c328
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c96
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.h15
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h584
-rw-r--r--drivers/scsi/qla2xxx/qla_dfs.c21
-rw-r--r--drivers/scsi/qla2xxx/qla_fw.h14
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h56
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c481
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c1251
-rw-r--r--drivers/scsi/qla2xxx/qla_inline.h45
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c358
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c840
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c886
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c516
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c1471
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c554
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h4
-rw-r--r--drivers/scsi/qlogicfas408.c2
-rw-r--r--drivers/scsi/scsi_error.c6
-rw-r--r--drivers/scsi/scsi_ioctl.c6
-rw-r--r--drivers/scsi/scsi_lib.c149
-rw-r--r--drivers/scsi/scsi_scan.c17
-rw-r--r--drivers/scsi/scsi_transport_fc.c13
-rw-r--r--drivers/scsi/scsi_transport_spi.c5
-rw-r--r--drivers/scsi/sd.c11
-rw-r--r--drivers/scsi/ses.c9
-rw-r--r--drivers/scsi/sr.c4
-rw-r--r--drivers/scsi/sr_ioctl.c2
-rw-r--r--drivers/scsi/st.c245
-rw-r--r--drivers/scsi/stex.c3
-rw-r--r--drivers/scsi/sym53c416.c2
-rw-r--r--drivers/scsi/tmscsim.c3
-rw-r--r--drivers/scsi/u14-34f.c3
-rw-r--r--drivers/scsi/wd7000.c4
-rw-r--r--drivers/serial/Kconfig25
-rw-r--r--drivers/serial/Makefile2
-rw-r--r--drivers/serial/amba-pl010.c2
-rw-r--r--drivers/serial/amba-pl011.c2
-rw-r--r--drivers/serial/imx.c8
-rw-r--r--drivers/serial/pxa.c3
-rw-r--r--drivers/serial/s3c24a0.c118
-rw-r--r--drivers/serial/s3c6400.c151
-rw-r--r--drivers/serial/samsung.c184
-rw-r--r--drivers/serial/samsung.h9
-rw-r--r--drivers/serial/serial_lh7a40x.c3
-rw-r--r--drivers/spi/pxa2xx_spi.c2
-rw-r--r--drivers/spi/spi_s3c24xx.c2
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c2
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c2
-rw-r--r--drivers/usb/gadget/s3c2410_udc.c4
-rw-r--r--drivers/usb/host/ehci-orion.c17
-rw-r--r--drivers/usb/host/ohci-omap.c8
-rw-r--r--drivers/usb/host/ohci-pxa27x.c2
-rw-r--r--drivers/video/Kconfig7
-rw-r--r--drivers/video/amba-clcd.c4
-rw-r--r--drivers/video/console/vgacon.c17
-rw-r--r--drivers/video/cyber2000fb.c2
-rw-r--r--drivers/video/imxfb.c468
-rw-r--r--drivers/video/imxfb.h73
-rw-r--r--drivers/video/pxafb.c981
-rw-r--r--drivers/video/pxafb.h82
-rw-r--r--drivers/video/sa1100fb.c2
-rw-r--r--drivers/virtio/virtio.c2
-rw-r--r--drivers/virtio/virtio_balloon.c13
-rw-r--r--drivers/virtio/virtio_pci.c43
-rw-r--r--drivers/virtio/virtio_ring.c3
-rw-r--r--drivers/watchdog/s3c2410_wdt.c2
-rw-r--r--drivers/watchdog/sa1100_wdt.c12
-rw-r--r--drivers/xen/events.c17
657 files changed, 82905 insertions, 19874 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index a67b8e7c712d..656448c7fef9 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1119,14 +1119,14 @@ static void ahci_start_port(struct ata_port *ap)
/* turn on LEDs */
if (ap->flags & ATA_FLAG_EM) {
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
emp = &pp->em_priv[link->pmp];
ahci_transmit_led_message(ap, emp->led_state, 4);
}
}
if (ap->flags & ATA_FLAG_SW_ACTIVITY)
- ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, EDGE)
ahci_init_sw_activity(link);
}
@@ -1361,7 +1361,7 @@ static ssize_t ahci_led_show(struct ata_port *ap, char *buf)
struct ahci_em_priv *emp;
int rc = 0;
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
emp = &pp->em_priv[link->pmp];
rc += sprintf(buf, "%lx\n", emp->led_state);
}
@@ -1941,7 +1941,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
u32 serror;
/* determine active link */
- ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, EDGE)
if (ata_link_active(link))
break;
if (!link)
diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c
index 5c33767e66de..dc48a6398abe 100644
--- a/drivers/ata/ata_generic.c
+++ b/drivers/ata/ata_generic.c
@@ -57,10 +57,7 @@ static int generic_set_mode(struct ata_link *link, struct ata_device **unused)
if (pdev->vendor == PCI_VENDOR_ID_CENATEK)
dma_enabled = 0xFF;
- ata_link_for_each_dev(dev, link) {
- if (!ata_dev_enabled(dev))
- continue;
-
+ ata_for_each_dev(dev, link, ENABLED) {
/* We don't really care */
dev->pio_mode = XFER_PIO_0;
dev->dma_mode = XFER_MW_DMA_0;
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index c11936e13dd3..5fdf1678d0cc 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -1072,20 +1072,13 @@ static int piix_broken_suspend(void)
* matching is necessary because dmi_system_id.matches is
* limited to four entries.
*/
- if (dmi_get_system_info(DMI_SYS_VENDOR) &&
- dmi_get_system_info(DMI_PRODUCT_NAME) &&
- dmi_get_system_info(DMI_PRODUCT_VERSION) &&
- dmi_get_system_info(DMI_PRODUCT_SERIAL) &&
- dmi_get_system_info(DMI_BOARD_VENDOR) &&
- dmi_get_system_info(DMI_BOARD_NAME) &&
- dmi_get_system_info(DMI_BOARD_VERSION) &&
- !strcmp(dmi_get_system_info(DMI_SYS_VENDOR), "TOSHIBA") &&
- !strcmp(dmi_get_system_info(DMI_PRODUCT_NAME), "000000") &&
- !strcmp(dmi_get_system_info(DMI_PRODUCT_VERSION), "000000") &&
- !strcmp(dmi_get_system_info(DMI_PRODUCT_SERIAL), "000000") &&
- !strcmp(dmi_get_system_info(DMI_BOARD_VENDOR), "TOSHIBA") &&
- !strcmp(dmi_get_system_info(DMI_BOARD_NAME), "Portable PC") &&
- !strcmp(dmi_get_system_info(DMI_BOARD_VERSION), "Version A0"))
+ if (dmi_match(DMI_SYS_VENDOR, "TOSHIBA") &&
+ dmi_match(DMI_PRODUCT_NAME, "000000") &&
+ dmi_match(DMI_PRODUCT_VERSION, "000000") &&
+ dmi_match(DMI_PRODUCT_SERIAL, "000000") &&
+ dmi_match(DMI_BOARD_VENDOR, "TOSHIBA") &&
+ dmi_match(DMI_BOARD_NAME, "Portable PC") &&
+ dmi_match(DMI_BOARD_VERSION, "Version A0"))
return 1;
return 0;
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index c012307d0ba6..ef02e488d468 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -89,7 +89,7 @@ void ata_acpi_associate_sata_port(struct ata_port *ap)
ap->link.device->acpi_handle = NULL;
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
acpi_integer adr = SATA_ADR(ap->port_no, link->pmp);
link->device->acpi_handle =
@@ -129,8 +129,8 @@ static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev)
struct ata_link *tlink;
struct ata_device *tdev;
- ata_port_for_each_link(tlink, ap)
- ata_link_for_each_dev(tdev, tlink)
+ ata_for_each_link(tlink, ap, EDGE)
+ ata_for_each_dev(tdev, tlink, ALL)
tdev->flags |= ATA_DFLAG_DETACH;
}
@@ -588,12 +588,9 @@ int ata_acpi_cbl_80wire(struct ata_port *ap, const struct ata_acpi_gtm *gtm)
{
struct ata_device *dev;
- ata_link_for_each_dev(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ENABLED) {
unsigned long xfer_mask, udma_mask;
- if (!ata_dev_enabled(dev))
- continue;
-
xfer_mask = ata_acpi_gtm_xfermask(dev, gtm);
ata_unpack_xfermask(xfer_mask, NULL, NULL, &udma_mask);
@@ -893,7 +890,7 @@ void ata_acpi_on_resume(struct ata_port *ap)
* use values set by _STM. Cache _GTF result and
* schedule _GTF.
*/
- ata_link_for_each_dev(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ALL) {
ata_acpi_clear_gtf(dev);
if (ata_dev_enabled(dev) &&
ata_dev_get_GTF(dev, NULL) >= 0)
@@ -904,7 +901,7 @@ void ata_acpi_on_resume(struct ata_port *ap)
* there's no reason to evaluate IDE _GTF early
* without _STM. Clear cache and schedule _GTF.
*/
- ata_link_for_each_dev(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ALL) {
ata_acpi_clear_gtf(dev);
if (ata_dev_enabled(dev))
dev->flags |= ATA_DFLAG_ACPI_PENDING;
@@ -932,8 +929,8 @@ void ata_acpi_set_state(struct ata_port *ap, pm_message_t state)
if (state.event == PM_EVENT_ON)
acpi_bus_set_power(ap->acpi_handle, ACPI_STATE_D0);
- ata_link_for_each_dev(dev, &ap->link) {
- if (dev->acpi_handle && ata_dev_enabled(dev))
+ ata_for_each_dev(dev, &ap->link, ENABLED) {
+ if (dev->acpi_handle)
acpi_bus_set_power(dev->acpi_handle,
state.event == PM_EVENT_ON ?
ACPI_STATE_D0 : ACPI_STATE_D3);
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index bc6695e3c848..fecca4223f8e 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -163,43 +163,119 @@ MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-/*
- * Iterator helpers. Don't use directly.
+/**
+ * ata_link_next - link iteration helper
+ * @link: the previous link, NULL to start
+ * @ap: ATA port containing links to iterate
+ * @mode: iteration mode, one of ATA_LITER_*
+ *
+ * LOCKING:
+ * Host lock or EH context.
*
- * LOCKING:
- * Host lock or EH context.
+ * RETURNS:
+ * Pointer to the next link.
*/
-struct ata_link *__ata_port_next_link(struct ata_port *ap,
- struct ata_link *link, bool dev_only)
+struct ata_link *ata_link_next(struct ata_link *link, struct ata_port *ap,
+ enum ata_link_iter_mode mode)
{
+ BUG_ON(mode != ATA_LITER_EDGE &&
+ mode != ATA_LITER_PMP_FIRST && mode != ATA_LITER_HOST_FIRST);
+
/* NULL link indicates start of iteration */
- if (!link) {
- if (dev_only && sata_pmp_attached(ap))
- return ap->pmp_link;
- return &ap->link;
- }
+ if (!link)
+ switch (mode) {
+ case ATA_LITER_EDGE:
+ case ATA_LITER_PMP_FIRST:
+ if (sata_pmp_attached(ap))
+ return ap->pmp_link;
+ /* fall through */
+ case ATA_LITER_HOST_FIRST:
+ return &ap->link;
+ }
- /* we just iterated over the host master link, what's next? */
- if (link == &ap->link) {
- if (!sata_pmp_attached(ap)) {
- if (unlikely(ap->slave_link) && !dev_only)
+ /* we just iterated over the host link, what's next? */
+ if (link == &ap->link)
+ switch (mode) {
+ case ATA_LITER_HOST_FIRST:
+ if (sata_pmp_attached(ap))
+ return ap->pmp_link;
+ /* fall through */
+ case ATA_LITER_PMP_FIRST:
+ if (unlikely(ap->slave_link))
return ap->slave_link;
+ /* fall through */
+ case ATA_LITER_EDGE:
return NULL;
}
- return ap->pmp_link;
- }
/* slave_link excludes PMP */
if (unlikely(link == ap->slave_link))
return NULL;
- /* iterate to the next PMP link */
+ /* we were over a PMP link */
if (++link < ap->pmp_link + ap->nr_pmp_links)
return link;
+
+ if (mode == ATA_LITER_PMP_FIRST)
+ return &ap->link;
+
return NULL;
}
/**
+ * ata_dev_next - device iteration helper
+ * @dev: the previous device, NULL to start
+ * @link: ATA link containing devices to iterate
+ * @mode: iteration mode, one of ATA_DITER_*
+ *
+ * LOCKING:
+ * Host lock or EH context.
+ *
+ * RETURNS:
+ * Pointer to the next device.
+ */
+struct ata_device *ata_dev_next(struct ata_device *dev, struct ata_link *link,
+ enum ata_dev_iter_mode mode)
+{
+ BUG_ON(mode != ATA_DITER_ENABLED && mode != ATA_DITER_ENABLED_REVERSE &&
+ mode != ATA_DITER_ALL && mode != ATA_DITER_ALL_REVERSE);
+
+ /* NULL dev indicates start of iteration */
+ if (!dev)
+ switch (mode) {
+ case ATA_DITER_ENABLED:
+ case ATA_DITER_ALL:
+ dev = link->device;
+ goto check;
+ case ATA_DITER_ENABLED_REVERSE:
+ case ATA_DITER_ALL_REVERSE:
+ dev = link->device + ata_link_max_devices(link) - 1;
+ goto check;
+ }
+
+ next:
+ /* move to the next one */
+ switch (mode) {
+ case ATA_DITER_ENABLED:
+ case ATA_DITER_ALL:
+ if (++dev < link->device + ata_link_max_devices(link))
+ goto check;
+ return NULL;
+ case ATA_DITER_ENABLED_REVERSE:
+ case ATA_DITER_ALL_REVERSE:
+ if (--dev >= link->device)
+ goto check;
+ return NULL;
+ }
+
+ check:
+ if ((mode == ATA_DITER_ENABLED || mode == ATA_DITER_ENABLED_REVERSE) &&
+ !ata_dev_enabled(dev))
+ goto next;
+ return dev;
+}
+
+/**
* ata_dev_phys_link - find physical link for a device
* @dev: ATA device to look up physical link for
*
@@ -1107,8 +1183,8 @@ static void ata_lpm_enable(struct ata_host *host)
for (i = 0; i < host->n_ports; i++) {
ap = host->ports[i];
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link)
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ALL)
ata_dev_disable_pm(dev);
}
}
@@ -2594,11 +2670,11 @@ int ata_bus_probe(struct ata_port *ap)
ata_port_probe(ap);
- ata_link_for_each_dev(dev, &ap->link)
+ ata_for_each_dev(dev, &ap->link, ALL)
tries[dev->devno] = ATA_PROBE_MAX_TRIES;
retry:
- ata_link_for_each_dev(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ALL) {
/* If we issue an SRST then an ATA drive (not ATAPI)
* may change configuration and be in PIO0 timing. If
* we do a hard reset (or are coming from power on)
@@ -2620,7 +2696,7 @@ int ata_bus_probe(struct ata_port *ap)
/* reset and determine device classes */
ap->ops->phy_reset(ap);
- ata_link_for_each_dev(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ALL) {
if (!(ap->flags & ATA_FLAG_DISABLED) &&
dev->class != ATA_DEV_UNKNOWN)
classes[dev->devno] = dev->class;
@@ -2636,7 +2712,7 @@ int ata_bus_probe(struct ata_port *ap)
specific sequence bass-ackwards so that PDIAG- is released by
the slave device */
- ata_link_for_each_dev_reverse(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ALL_REVERSE) {
if (tries[dev->devno])
dev->class = classes[dev->devno];
@@ -2653,24 +2729,19 @@ int ata_bus_probe(struct ata_port *ap)
if (ap->ops->cable_detect)
ap->cbl = ap->ops->cable_detect(ap);
- /* We may have SATA bridge glue hiding here irrespective of the
- reported cable types and sensed types */
- ata_link_for_each_dev(dev, &ap->link) {
- if (!ata_dev_enabled(dev))
- continue;
- /* SATA drives indicate we have a bridge. We don't know which
- end of the link the bridge is which is a problem */
+ /* We may have SATA bridge glue hiding here irrespective of
+ * the reported cable types and sensed types. When SATA
+ * drives indicate we have a bridge, we don't know which end
+ * of the link the bridge is which is a problem.
+ */
+ ata_for_each_dev(dev, &ap->link, ENABLED)
if (ata_id_is_sata(dev->id))
ap->cbl = ATA_CBL_SATA;
- }
/* After the identify sequence we can now set up the devices. We do
this in the normal order so that the user doesn't get confused */
- ata_link_for_each_dev(dev, &ap->link) {
- if (!ata_dev_enabled(dev))
- continue;
-
+ ata_for_each_dev(dev, &ap->link, ENABLED) {
ap->link.eh_context.i.flags |= ATA_EHI_PRINTINFO;
rc = ata_dev_configure(dev);
ap->link.eh_context.i.flags &= ~ATA_EHI_PRINTINFO;
@@ -2683,9 +2754,8 @@ int ata_bus_probe(struct ata_port *ap)
if (rc)
goto fail;
- ata_link_for_each_dev(dev, &ap->link)
- if (ata_dev_enabled(dev))
- return 0;
+ ata_for_each_dev(dev, &ap->link, ENABLED)
+ return 0;
/* no device present, disable port */
ata_port_disable(ap);
@@ -3331,13 +3401,10 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
int rc = 0, used_dma = 0, found = 0;
/* step 1: calculate xfer_mask */
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ENABLED) {
unsigned long pio_mask, dma_mask;
unsigned int mode_mask;
- if (!ata_dev_enabled(dev))
- continue;
-
mode_mask = ATA_DMA_MASK_ATA;
if (dev->class == ATA_DEV_ATAPI)
mode_mask = ATA_DMA_MASK_ATAPI;
@@ -3366,10 +3433,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
goto out;
/* step 2: always set host PIO timings */
- ata_link_for_each_dev(dev, link) {
- if (!ata_dev_enabled(dev))
- continue;
-
+ ata_for_each_dev(dev, link, ENABLED) {
if (dev->pio_mode == 0xff) {
ata_dev_printk(dev, KERN_WARNING, "no PIO support\n");
rc = -EINVAL;
@@ -3383,8 +3447,8 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
}
/* step 3: set host DMA timings */
- ata_link_for_each_dev(dev, link) {
- if (!ata_dev_enabled(dev) || !ata_dma_enabled(dev))
+ ata_for_each_dev(dev, link, ENABLED) {
+ if (!ata_dma_enabled(dev))
continue;
dev->xfer_mode = dev->dma_mode;
@@ -3394,11 +3458,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
}
/* step 4: update devices' xfer mode */
- ata_link_for_each_dev(dev, link) {
- /* don't update suspended devices' xfer mode */
- if (!ata_dev_enabled(dev))
- continue;
-
+ ata_for_each_dev(dev, link, ENABLED) {
rc = ata_dev_set_mode(dev);
if (rc)
goto out;
@@ -4048,6 +4108,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
{ "Maxtor 7V300F0", "VA111630", ATA_HORKAGE_NONCQ },
{ "ST380817AS", "3.42", ATA_HORKAGE_NONCQ },
{ "ST3160023AS", "3.42", ATA_HORKAGE_NONCQ },
+ { "OCZ CORE_SSD", "02.10104", ATA_HORKAGE_NONCQ },
/* Seagate NCQ + FLUSH CACHE firmware bug */
{ "ST31500341AS", "SD15", ATA_HORKAGE_NONCQ |
@@ -4263,9 +4324,9 @@ static int cable_is_40wire(struct ata_port *ap)
* - if you have a non detect capable drive you don't want it
* to colour the choice
*/
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev) && !ata_is_40wire(dev))
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ENABLED) {
+ if (!ata_is_40wire(dev))
return 0;
}
}
@@ -4672,7 +4733,6 @@ static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap)
/**
* ata_qc_new_init - Request an available ATA command, and initialize it
* @dev: Device from whom we request an available command structure
- * @tag: command tag
*
* LOCKING:
* None.
@@ -5218,7 +5278,7 @@ static int ata_host_request_pm(struct ata_host *host, pm_message_t mesg,
}
ap->pflags |= ATA_PFLAG_PM_PENDING;
- __ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, HOST_FIRST) {
link->eh_info.action |= action;
link->eh_info.flags |= ehi_flags;
}
@@ -6047,8 +6107,6 @@ int ata_host_activate(struct ata_host *host, int irq,
static void ata_port_detach(struct ata_port *ap)
{
unsigned long flags;
- struct ata_link *link;
- struct ata_device *dev;
if (!ap->ops->error_handler)
goto skip_eh;
@@ -6056,28 +6114,15 @@ static void ata_port_detach(struct ata_port *ap)
/* tell EH we're leaving & flush EH */
spin_lock_irqsave(ap->lock, flags);
ap->pflags |= ATA_PFLAG_UNLOADING;
+ ata_port_schedule_eh(ap);
spin_unlock_irqrestore(ap->lock, flags);
+ /* wait till EH commits suicide */
ata_port_wait_eh(ap);
- /* EH is now guaranteed to see UNLOADING - EH context belongs
- * to us. Restore SControl and disable all existing devices.
- */
- __ata_port_for_each_link(link, ap) {
- sata_scr_write(link, SCR_CONTROL, link->saved_scontrol & 0xff0);
- ata_link_for_each_dev(dev, link)
- ata_dev_disable(dev);
- }
+ /* it better be dead now */
+ WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED));
- /* Final freeze & EH. All in-flight commands are aborted. EH
- * will be skipped and retrials will be terminated with bad
- * target.
- */
- spin_lock_irqsave(ap->lock, flags);
- ata_port_freeze(ap); /* won't be thawed */
- spin_unlock_irqrestore(ap->lock, flags);
-
- ata_port_wait_eh(ap);
cancel_rearming_delayed_work(&ap->hotplug_task);
skip_eh:
@@ -6528,7 +6573,8 @@ EXPORT_SYMBOL_GPL(ata_base_port_ops);
EXPORT_SYMBOL_GPL(sata_port_ops);
EXPORT_SYMBOL_GPL(ata_dummy_port_ops);
EXPORT_SYMBOL_GPL(ata_dummy_port_info);
-EXPORT_SYMBOL_GPL(__ata_port_next_link);
+EXPORT_SYMBOL_GPL(ata_link_next);
+EXPORT_SYMBOL_GPL(ata_dev_next);
EXPORT_SYMBOL_GPL(ata_std_bios_param);
EXPORT_SYMBOL_GPL(ata_host_init);
EXPORT_SYMBOL_GPL(ata_host_alloc);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 32da9a93ce44..8147a8386370 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -422,7 +422,7 @@ static void ata_eh_clear_action(struct ata_link *link, struct ata_device *dev,
if (!dev) {
ehi->action &= ~action;
- ata_link_for_each_dev(tdev, link)
+ ata_for_each_dev(tdev, link, ALL)
ehi->dev_action[tdev->devno] &= ~action;
} else {
/* doesn't make sense for port-wide EH actions */
@@ -430,7 +430,7 @@ static void ata_eh_clear_action(struct ata_link *link, struct ata_device *dev,
/* break ehi->action into ehi->dev_action */
if (ehi->action & action) {
- ata_link_for_each_dev(tdev, link)
+ ata_for_each_dev(tdev, link, ALL)
ehi->dev_action[tdev->devno] |=
ehi->action & action;
ehi->action &= ~action;
@@ -491,6 +491,31 @@ enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd)
return ret;
}
+static void ata_eh_unload(struct ata_port *ap)
+{
+ struct ata_link *link;
+ struct ata_device *dev;
+ unsigned long flags;
+
+ /* Restore SControl IPM and SPD for the next driver and
+ * disable attached devices.
+ */
+ ata_for_each_link(link, ap, PMP_FIRST) {
+ sata_scr_write(link, SCR_CONTROL, link->saved_scontrol & 0xff0);
+ ata_for_each_dev(dev, link, ALL)
+ ata_dev_disable(dev);
+ }
+
+ /* freeze and set UNLOADED */
+ spin_lock_irqsave(ap->lock, flags);
+
+ ata_port_freeze(ap); /* won't be thawed */
+ ap->pflags &= ~ATA_PFLAG_EH_PENDING; /* clear pending from freeze */
+ ap->pflags |= ATA_PFLAG_UNLOADED;
+
+ spin_unlock_irqrestore(ap->lock, flags);
+}
+
/**
* ata_scsi_error - SCSI layer error handler callback
* @host: SCSI host on which error occurred
@@ -592,7 +617,7 @@ void ata_scsi_error(struct Scsi_Host *host)
/* fetch & clear EH info */
spin_lock_irqsave(ap->lock, flags);
- __ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, HOST_FIRST) {
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev;
@@ -600,12 +625,9 @@ void ata_scsi_error(struct Scsi_Host *host)
link->eh_context.i = link->eh_info;
memset(&link->eh_info, 0, sizeof(link->eh_info));
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ENABLED) {
int devno = dev->devno;
- if (!ata_dev_enabled(dev))
- continue;
-
ehc->saved_xfer_mode[devno] = dev->xfer_mode;
if (ata_ncq_enabled(dev))
ehc->saved_ncq_enabled |= 1 << devno;
@@ -621,8 +643,13 @@ void ata_scsi_error(struct Scsi_Host *host)
/* invoke EH, skip if unloading or suspended */
if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED)))
ap->ops->error_handler(ap);
- else
+ else {
+ /* if unloading, commence suicide */
+ if ((ap->pflags & ATA_PFLAG_UNLOADING) &&
+ !(ap->pflags & ATA_PFLAG_UNLOADED))
+ ata_eh_unload(ap);
ata_eh_finish(ap);
+ }
/* process port suspend request */
ata_eh_handle_port_suspend(ap);
@@ -644,7 +671,7 @@ void ata_scsi_error(struct Scsi_Host *host)
}
/* this run is complete, make sure EH info is clear */
- __ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, HOST_FIRST)
memset(&link->eh_info, 0, sizeof(link->eh_info));
/* Clear host_eh_scheduled while holding ap->lock such
@@ -1025,7 +1052,7 @@ int sata_async_notification(struct ata_port *ap)
struct ata_link *link;
/* check and notify ATAPI AN */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
if (!(sntf & (1 << link->pmp)))
continue;
@@ -2005,7 +2032,7 @@ void ata_eh_autopsy(struct ata_port *ap)
{
struct ata_link *link;
- ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, EDGE)
ata_eh_link_autopsy(link);
/* Handle the frigging slave link. Autopsy is done similarly
@@ -2219,7 +2246,7 @@ void ata_eh_report(struct ata_port *ap)
{
struct ata_link *link;
- __ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, HOST_FIRST)
ata_eh_link_report(link);
}
@@ -2230,7 +2257,7 @@ static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset,
struct ata_device *dev;
if (clear_classes)
- ata_link_for_each_dev(dev, link)
+ ata_for_each_dev(dev, link, ALL)
classes[dev->devno] = ATA_DEV_UNKNOWN;
return reset(link, classes, deadline);
@@ -2294,7 +2321,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
ata_eh_about_to_do(link, NULL, ATA_EH_RESET);
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
/* If we issue an SRST then an ATA drive (not ATAPI)
* may change configuration and be in PIO0 timing. If
* we do a hard reset (or are coming from power on)
@@ -2355,7 +2382,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
"port disabled. ignoring.\n");
ehc->i.action &= ~ATA_EH_RESET;
- ata_link_for_each_dev(dev, link)
+ ata_for_each_dev(dev, link, ALL)
classes[dev->devno] = ATA_DEV_NONE;
rc = 0;
@@ -2369,7 +2396,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
* bang classes and return.
*/
if (reset && !(ehc->i.action & ATA_EH_RESET)) {
- ata_link_for_each_dev(dev, link)
+ ata_for_each_dev(dev, link, ALL)
classes[dev->devno] = ATA_DEV_NONE;
rc = 0;
goto out;
@@ -2454,7 +2481,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
/*
* Post-reset processing
*/
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
/* After the reset, the device state is PIO 0 and the
* controller state is undefined. Reset also wakes up
* drives from sleeping mode.
@@ -2510,7 +2537,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
* can be reliably detected and retried.
*/
nr_unknown = 0;
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
/* convert all ATA_DEV_UNKNOWN to ATA_DEV_NONE */
if (classes[dev->devno] == ATA_DEV_UNKNOWN) {
classes[dev->devno] = ATA_DEV_NONE;
@@ -2619,8 +2646,8 @@ static inline void ata_eh_pull_park_action(struct ata_port *ap)
spin_lock_irqsave(ap->lock, flags);
INIT_COMPLETION(ap->park_req_pending);
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ALL) {
struct ata_eh_info *ehi = &link->eh_info;
link->eh_context.i.dev_action[dev->devno] |=
@@ -2675,7 +2702,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
* be done backwards such that PDIAG- is released by the slave
* device before the master device is identified.
*/
- ata_link_for_each_dev_reverse(dev, link) {
+ ata_for_each_dev(dev, link, ALL_REVERSE) {
unsigned int action = ata_eh_dev_action(dev);
unsigned int readid_flags = 0;
@@ -2744,7 +2771,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
/* Configure new devices forward such that user doesn't see
* device detection messages backwards.
*/
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
if (!(new_mask & (1 << dev->devno)) ||
dev->class == ATA_DEV_PMP)
continue;
@@ -2793,10 +2820,7 @@ int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
int rc;
/* if data transfer is verified, clear DUBIOUS_XFER on ering top */
- ata_link_for_each_dev(dev, link) {
- if (!ata_dev_enabled(dev))
- continue;
-
+ ata_for_each_dev(dev, link, ENABLED) {
if (!(dev->flags & ATA_DFLAG_DUBIOUS_XFER)) {
struct ata_ering_entry *ent;
@@ -2813,14 +2837,11 @@ int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
rc = ata_do_set_mode(link, r_failed_dev);
/* if transfer mode has changed, set DUBIOUS_XFER on device */
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ENABLED) {
struct ata_eh_context *ehc = &link->eh_context;
u8 saved_xfer_mode = ehc->saved_xfer_mode[dev->devno];
u8 saved_ncq = !!(ehc->saved_ncq_enabled & (1 << dev->devno));
- if (!ata_dev_enabled(dev))
- continue;
-
if (dev->xfer_mode != saved_xfer_mode ||
ata_ncq_enabled(dev) != saved_ncq)
dev->flags |= ATA_DFLAG_DUBIOUS_XFER;
@@ -2881,9 +2902,8 @@ static int ata_link_nr_enabled(struct ata_link *link)
struct ata_device *dev;
int cnt = 0;
- ata_link_for_each_dev(dev, link)
- if (ata_dev_enabled(dev))
- cnt++;
+ ata_for_each_dev(dev, link, ENABLED)
+ cnt++;
return cnt;
}
@@ -2892,7 +2912,7 @@ static int ata_link_nr_vacant(struct ata_link *link)
struct ata_device *dev;
int cnt = 0;
- ata_link_for_each_dev(dev, link)
+ ata_for_each_dev(dev, link, ALL)
if (dev->class == ATA_DEV_UNKNOWN)
cnt++;
return cnt;
@@ -2918,7 +2938,7 @@ static int ata_eh_skip_recovery(struct ata_link *link)
return 0;
/* skip if class codes for all vacant slots are ATA_DEV_NONE */
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
if (dev->class == ATA_DEV_UNKNOWN &&
ehc->classes[dev->devno] != ATA_DEV_NONE)
return 0;
@@ -3026,7 +3046,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
DPRINTK("ENTER\n");
/* prep for recovery */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
/* re-enable link? */
@@ -3038,7 +3058,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
ata_eh_done(link, NULL, ATA_EH_ENABLE_LINK);
}
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
if (link->flags & ATA_LFLAG_NO_RETRY)
ehc->tries[dev->devno] = 1;
else
@@ -3068,19 +3088,19 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
goto out;
/* prep for EH */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
/* skip EH if possible. */
if (ata_eh_skip_recovery(link))
ehc->i.action = 0;
- ata_link_for_each_dev(dev, link)
+ ata_for_each_dev(dev, link, ALL)
ehc->classes[dev->devno] = ATA_DEV_UNKNOWN;
}
/* reset */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
if (!(ehc->i.action & ATA_EH_RESET))
@@ -3105,8 +3125,8 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
ata_eh_pull_park_action(ap);
deadline = jiffies;
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ALL) {
struct ata_eh_context *ehc = &link->eh_context;
unsigned long tmp;
@@ -3134,8 +3154,8 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
deadline = wait_for_completion_timeout(&ap->park_req_pending,
deadline - now);
} while (deadline);
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ALL) {
if (!(link->eh_context.unloaded_mask &
(1 << dev->devno)))
continue;
@@ -3146,7 +3166,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
}
/* the rest */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
/* revalidate existing devices and attach new ones */
@@ -3172,7 +3192,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
* disrupting the current users of the device.
*/
if (ehc->i.flags & ATA_EHI_DID_RESET) {
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
if (dev->class != ATA_DEV_ATAPI)
continue;
rc = atapi_eh_clear_ua(dev);
@@ -3183,7 +3203,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
/* configure link power saving */
if (ehc->i.action & ATA_EH_LPM)
- ata_link_for_each_dev(dev, link)
+ ata_for_each_dev(dev, link, ALL)
ata_dev_enable_pm(dev, ap->pm_policy);
/* this link is okay now */
@@ -3288,7 +3308,7 @@ void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset,
rc = ata_eh_recover(ap, prereset, softreset, hardreset, postreset,
NULL);
if (rc) {
- ata_link_for_each_dev(dev, &ap->link)
+ ata_for_each_dev(dev, &ap->link, ALL)
ata_dev_disable(dev);
}
diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c
index b65db309c181..98ca07a2db87 100644
--- a/drivers/ata/libata-pmp.c
+++ b/drivers/ata/libata-pmp.c
@@ -321,7 +321,7 @@ static void sata_pmp_quirks(struct ata_port *ap)
if (vendor == 0x1095 && devid == 0x3726) {
/* sil3726 quirks */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
/* Class code report is unreliable and SRST
* times out under certain configurations.
*/
@@ -336,7 +336,7 @@ static void sata_pmp_quirks(struct ata_port *ap)
}
} else if (vendor == 0x1095 && devid == 0x4723) {
/* sil4723 quirks */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
/* class code report is unreliable */
if (link->pmp < 2)
link->flags |= ATA_LFLAG_ASSUME_ATA;
@@ -348,7 +348,7 @@ static void sata_pmp_quirks(struct ata_port *ap)
}
} else if (vendor == 0x1095 && devid == 0x4726) {
/* sil4726 quirks */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
/* Class code report is unreliable and SRST
* times out under certain configurations.
* Config device can be at port 0 or 5 and
@@ -450,7 +450,7 @@ int sata_pmp_attach(struct ata_device *dev)
if (ap->ops->pmp_attach)
ap->ops->pmp_attach(ap);
- ata_port_for_each_link(tlink, ap)
+ ata_for_each_link(tlink, ap, EDGE)
sata_link_init_spd(tlink);
ata_acpi_associate_sata_port(ap);
@@ -487,7 +487,7 @@ static void sata_pmp_detach(struct ata_device *dev)
if (ap->ops->pmp_detach)
ap->ops->pmp_detach(ap);
- ata_port_for_each_link(tlink, ap)
+ ata_for_each_link(tlink, ap, EDGE)
ata_eh_detach_dev(tlink->device);
spin_lock_irqsave(ap->lock, flags);
@@ -700,7 +700,7 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
}
/* PMP is reset, SErrors cannot be trusted, scan all */
- ata_port_for_each_link(tlink, ap) {
+ ata_for_each_link(tlink, ap, EDGE) {
struct ata_eh_context *ehc = &tlink->eh_context;
ehc->i.probe_mask |= ATA_ALL_DEVICES;
@@ -768,7 +768,7 @@ static int sata_pmp_eh_handle_disabled_links(struct ata_port *ap)
spin_lock_irqsave(ap->lock, flags);
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
if (!(link->flags & ATA_LFLAG_DISABLED))
continue;
@@ -852,7 +852,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
int cnt, rc;
pmp_tries = ATA_EH_PMP_TRIES;
- ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, EDGE)
link_tries[link->pmp] = ATA_EH_PMP_LINK_TRIES;
retry:
@@ -861,7 +861,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
rc = ata_eh_recover(ap, ops->prereset, ops->softreset,
ops->hardreset, ops->postreset, NULL);
if (rc) {
- ata_link_for_each_dev(dev, &ap->link)
+ ata_for_each_dev(dev, &ap->link, ALL)
ata_dev_disable(dev);
return rc;
}
@@ -870,7 +870,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
return 0;
/* new PMP online */
- ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, EDGE)
link_tries[link->pmp] = ATA_EH_PMP_LINK_TRIES;
/* fall through */
@@ -942,7 +942,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
}
cnt = 0;
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
if (!(gscr_error & (1 << link->pmp)))
continue;
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 47c7afcb36f2..4040d8b53216 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -517,7 +517,7 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg)
/* Good values for timeout and retries? Values below
from scsi_ioctl_send_command() for default case... */
cmd_result = scsi_execute(scsidev, scsi_cmd, data_dir, argbuf, argsize,
- sensebuf, (10*HZ), 5, 0);
+ sensebuf, (10*HZ), 5, 0, NULL);
if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */
u8 *desc = sensebuf + 8;
@@ -603,7 +603,7 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg)
/* Good values for timeout and retries? Values below
from scsi_ioctl_send_command() for default case... */
cmd_result = scsi_execute(scsidev, scsi_cmd, DMA_NONE, NULL, 0,
- sensebuf, (10*HZ), 5, 0);
+ sensebuf, (10*HZ), 5, 0, NULL);
if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */
u8 *desc = sensebuf + 8;
@@ -3229,12 +3229,12 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
return;
repeat:
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ENABLED) {
struct scsi_device *sdev;
int channel = 0, id = 0;
- if (!ata_dev_enabled(dev) || dev->sdev)
+ if (dev->sdev)
continue;
if (ata_is_host_link(link))
@@ -3255,9 +3255,9 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
* failure occurred, scan would have failed silently. Check
* whether all devices are attached.
*/
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev) && !dev->sdev)
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ENABLED) {
+ if (!dev->sdev)
goto exit_loop;
}
}
@@ -3381,7 +3381,7 @@ static void ata_scsi_handle_link_detach(struct ata_link *link)
struct ata_port *ap = link->ap;
struct ata_device *dev;
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
unsigned long flags;
if (!(dev->flags & ATA_DFLAG_DETACHED))
@@ -3496,7 +3496,7 @@ static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
if (devno == SCAN_WILD_CARD) {
struct ata_link *link;
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
struct ata_eh_info *ehi = &link->eh_info;
ehi->probe_mask |= ATA_ALL_DEVICES;
ehi->action |= ATA_EH_RESET;
@@ -3544,11 +3544,11 @@ void ata_scsi_dev_rescan(struct work_struct *work)
spin_lock_irqsave(ap->lock, flags);
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ENABLED) {
struct scsi_device *sdev = dev->sdev;
- if (!ata_dev_enabled(dev) || !sdev)
+ if (!sdev)
continue;
if (scsi_device_get(sdev))
continue;
diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c
index 1266924c11f9..1050fed96b2b 100644
--- a/drivers/ata/pata_bf54x.c
+++ b/drivers/ata/pata_bf54x.c
@@ -356,7 +356,6 @@ static void bfin_set_piomode(struct ata_port *ap, struct ata_device *adev)
* bfin_set_dmamode - Initialize host controller PATA DMA timings
* @ap: Port whose timings we are configuring
* @adev: um
- * @udma: udma mode, 0 - 6
*
* Set UDMA mode for device.
*
diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
index 860ede526282..f828a29d7756 100644
--- a/drivers/ata/pata_it821x.c
+++ b/drivers/ata/pata_it821x.c
@@ -465,24 +465,22 @@ static int it821x_smart_set_mode(struct ata_link *link, struct ata_device **unus
{
struct ata_device *dev;
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
- /* We don't really care */
- dev->pio_mode = XFER_PIO_0;
- dev->dma_mode = XFER_MW_DMA_0;
- /* We do need the right mode information for DMA or PIO
- and this comes from the current configuration flags */
- if (ata_id_has_dma(dev->id)) {
- ata_dev_printk(dev, KERN_INFO, "configured for DMA\n");
- dev->xfer_mode = XFER_MW_DMA_0;
- dev->xfer_shift = ATA_SHIFT_MWDMA;
- dev->flags &= ~ATA_DFLAG_PIO;
- } else {
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
- dev->xfer_mode = XFER_PIO_0;
- dev->xfer_shift = ATA_SHIFT_PIO;
- dev->flags |= ATA_DFLAG_PIO;
- }
+ ata_for_each_dev(dev, link, ENABLED) {
+ /* We don't really care */
+ dev->pio_mode = XFER_PIO_0;
+ dev->dma_mode = XFER_MW_DMA_0;
+ /* We do need the right mode information for DMA or PIO
+ and this comes from the current configuration flags */
+ if (ata_id_has_dma(dev->id)) {
+ ata_dev_printk(dev, KERN_INFO, "configured for DMA\n");
+ dev->xfer_mode = XFER_MW_DMA_0;
+ dev->xfer_shift = ATA_SHIFT_MWDMA;
+ dev->flags &= ~ATA_DFLAG_PIO;
+ } else {
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
}
}
return 0;
diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c
index 2014253f6c88..b173c157ab00 100644
--- a/drivers/ata/pata_ixp4xx_cf.c
+++ b/drivers/ata/pata_ixp4xx_cf.c
@@ -30,14 +30,12 @@ static int ixp4xx_set_mode(struct ata_link *link, struct ata_device **error)
{
struct ata_device *dev;
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
- ata_dev_printk(dev, KERN_INFO, "configured for PIO0\n");
- dev->pio_mode = XFER_PIO_0;
- dev->xfer_mode = XFER_PIO_0;
- dev->xfer_shift = ATA_SHIFT_PIO;
- dev->flags |= ATA_DFLAG_PIO;
- }
+ ata_for_each_dev(dev, link, ENABLED) {
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO0\n");
+ dev->pio_mode = XFER_PIO_0;
+ dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
}
return 0;
}
diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c
index 930c2208640b..6c1d778b63a9 100644
--- a/drivers/ata/pata_legacy.c
+++ b/drivers/ata/pata_legacy.c
@@ -194,15 +194,12 @@ static int legacy_set_mode(struct ata_link *link, struct ata_device **unused)
{
struct ata_device *dev;
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
- ata_dev_printk(dev, KERN_INFO,
- "configured for PIO\n");
- dev->pio_mode = XFER_PIO_0;
- dev->xfer_mode = XFER_PIO_0;
- dev->xfer_shift = ATA_SHIFT_PIO;
- dev->flags |= ATA_DFLAG_PIO;
- }
+ ata_for_each_dev(dev, link, ENABLED) {
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ dev->pio_mode = XFER_PIO_0;
+ dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
}
return 0;
}
@@ -641,7 +638,6 @@ static void qdi6500_set_piomode(struct ata_port *ap, struct ata_device *adev)
* qdi6580dp_set_piomode - PIO setup for dual channel
* @ap: Port
* @adev: Device
- * @irq: interrupt line
*
* In dual channel mode the 6580 has one clock per channel and we have
* to software clockswitch in qc_issue.
@@ -1028,7 +1024,7 @@ static __init int legacy_init_one(struct legacy_probe *probe)
/* Nothing found means we drop the port as its probably not there */
ret = -ENODEV;
- ata_link_for_each_dev(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ALL) {
if (!ata_dev_absent(dev)) {
legacy_host[probe->slot] = host;
ld->platform_dev = pdev;
diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c
index c0dbc46a348e..2c1a91c40c1a 100644
--- a/drivers/ata/pata_oldpiix.c
+++ b/drivers/ata/pata_oldpiix.c
@@ -116,7 +116,6 @@ static void oldpiix_set_piomode (struct ata_port *ap, struct ata_device *adev)
* oldpiix_set_dmamode - Initialize host controller PATA DMA timings
* @ap: Port whose timings we are configuring
* @adev: Device to program
- * @isich: True if the device is an ICH and has IOCFG registers
*
* Set MWDMA mode for device, in host controller PCI config space.
*
diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c
index 0e1c2c1134d3..e94efccaa482 100644
--- a/drivers/ata/pata_pdc2027x.c
+++ b/drivers/ata/pata_pdc2027x.c
@@ -281,7 +281,6 @@ static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long
* pdc2027x_set_piomode - Initialize host controller PATA PIO timings
* @ap: Port to configure
* @adev: um
- * @pio: PIO mode, 0 - 4
*
* Set PIO mode for device.
*
@@ -326,7 +325,6 @@ static void pdc2027x_set_piomode(struct ata_port *ap, struct ata_device *adev)
* pdc2027x_set_dmamode - Initialize host controller PATA UDMA timings
* @ap: Port to configure
* @adev: um
- * @udma: udma mode, XFER_UDMA_0 to XFER_UDMA_6
*
* Set UDMA mode for device.
*
@@ -406,23 +404,20 @@ static int pdc2027x_set_mode(struct ata_link *link, struct ata_device **r_failed
if (rc < 0)
return rc;
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
+ ata_for_each_dev(dev, link, ENABLED) {
+ pdc2027x_set_piomode(ap, dev);
- pdc2027x_set_piomode(ap, dev);
+ /*
+ * Enable prefetch if the device support PIO only.
+ */
+ if (dev->xfer_shift == ATA_SHIFT_PIO) {
+ u32 ctcr1 = ioread32(dev_mmio(ap, dev, PDC_CTCR1));
+ ctcr1 |= (1 << 25);
+ iowrite32(ctcr1, dev_mmio(ap, dev, PDC_CTCR1));
- /*
- * Enable prefetch if the device support PIO only.
- */
- if (dev->xfer_shift == ATA_SHIFT_PIO) {
- u32 ctcr1 = ioread32(dev_mmio(ap, dev, PDC_CTCR1));
- ctcr1 |= (1 << 25);
- iowrite32(ctcr1, dev_mmio(ap, dev, PDC_CTCR1));
-
- PDPRINTK("Turn on prefetch\n");
- } else {
- pdc2027x_set_dmamode(ap, dev);
- }
+ PDPRINTK("Turn on prefetch\n");
+ } else {
+ pdc2027x_set_dmamode(ap, dev);
}
}
return 0;
diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c
index 77e4e3b17f54..6afa07a37648 100644
--- a/drivers/ata/pata_platform.c
+++ b/drivers/ata/pata_platform.c
@@ -34,14 +34,12 @@ static int pata_platform_set_mode(struct ata_link *link, struct ata_device **unu
{
struct ata_device *dev;
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
- /* We don't really care */
- dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
- dev->xfer_shift = ATA_SHIFT_PIO;
- dev->flags |= ATA_DFLAG_PIO;
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
- }
+ ata_for_each_dev(dev, link, ENABLED) {
+ /* We don't really care */
+ dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
}
return 0;
}
diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c
index 0b0aa452de14..695d44ae52c6 100644
--- a/drivers/ata/pata_radisys.c
+++ b/drivers/ata/pata_radisys.c
@@ -81,7 +81,6 @@ static void radisys_set_piomode (struct ata_port *ap, struct ata_device *adev)
* radisys_set_dmamode - Initialize host controller PATA DMA timings
* @ap: Port whose timings we are configuring
* @adev: Device to program
- * @isich: True if the device is an ICH and has IOCFG registers
*
* Set MWDMA mode for device, in host controller PCI config space.
*
diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c
index 7dfd1f3f6f3a..46d6bc1bf1e9 100644
--- a/drivers/ata/pata_rz1000.c
+++ b/drivers/ata/pata_rz1000.c
@@ -38,15 +38,13 @@ static int rz1000_set_mode(struct ata_link *link, struct ata_device **unused)
{
struct ata_device *dev;
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
- /* We don't really care */
- dev->pio_mode = XFER_PIO_0;
- dev->xfer_mode = XFER_PIO_0;
- dev->xfer_shift = ATA_SHIFT_PIO;
- dev->flags |= ATA_DFLAG_PIO;
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
- }
+ ata_for_each_dev(dev, link, ENABLED) {
+ /* We don't really care */
+ dev->pio_mode = XFER_PIO_0;
+ dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
}
return 0;
}
diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c
index cf3707e516a2..d447f1cb46ec 100644
--- a/drivers/ata/pata_scc.c
+++ b/drivers/ata/pata_scc.c
@@ -210,7 +210,6 @@ static void scc_set_piomode (struct ata_port *ap, struct ata_device *adev)
* scc_set_dmamode - Initialize host controller PATA DMA timings
* @ap: Port whose timings we are configuring
* @adev: um
- * @udma: udma mode, 0 - 6
*
* Set UDMA mode for device.
*
diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c
index 72e41c9f969b..8d2fd9dd40c7 100644
--- a/drivers/ata/pata_serverworks.c
+++ b/drivers/ata/pata_serverworks.c
@@ -138,7 +138,6 @@ static struct sv_cable_table cable_detect[] = {
/**
* serverworks_cable_detect - cable detection
* @ap: ATA port
- * @deadline: deadline jiffies for the operation
*
* Perform cable detection according to the device and subvendor
* identifications
diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c
index e4be55e047f6..27ceb42a774b 100644
--- a/drivers/ata/pata_sis.c
+++ b/drivers/ata/pata_sis.c
@@ -112,7 +112,6 @@ static int sis_133_cable_detect(struct ata_port *ap)
/**
* sis_66_cable_detect - check for 40/80 pin
* @ap: Port
- * @deadline: deadline jiffies for the operation
*
* Perform cable detection on the UDMA66, UDMA100 and early UDMA133
* SiS IDE controllers.
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 2b24ae58b52e..86918634a4c5 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -1836,7 +1836,6 @@ static void mv_unexpected_intr(struct ata_port *ap, int edma_was_enabled)
/**
* mv_err_intr - Handle error interrupts on the port
* @ap: ATA channel to manipulate
- * @qc: affected command (non-NCQ), or NULL
*
* Most cases require a full reset of the chip's state machine,
* which also performs a COMRESET.
diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c
index 031d7b7dee34..564c142b03b0 100644
--- a/drivers/ata/sata_sil.c
+++ b/drivers/ata/sata_sil.c
@@ -46,7 +46,9 @@
#include <linux/libata.h>
#define DRV_NAME "sata_sil"
-#define DRV_VERSION "2.3"
+#define DRV_VERSION "2.4"
+
+#define SIL_DMA_BOUNDARY 0x7fffffffUL
enum {
SIL_MMIO_BAR = 5,
@@ -118,6 +120,10 @@ static void sil_dev_config(struct ata_device *dev);
static int sil_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
static int sil_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
static int sil_set_mode(struct ata_link *link, struct ata_device **r_failed);
+static void sil_qc_prep(struct ata_queued_cmd *qc);
+static void sil_bmdma_setup(struct ata_queued_cmd *qc);
+static void sil_bmdma_start(struct ata_queued_cmd *qc);
+static void sil_bmdma_stop(struct ata_queued_cmd *qc);
static void sil_freeze(struct ata_port *ap);
static void sil_thaw(struct ata_port *ap);
@@ -167,13 +173,22 @@ static struct pci_driver sil_pci_driver = {
};
static struct scsi_host_template sil_sht = {
- ATA_BMDMA_SHT(DRV_NAME),
+ ATA_BASE_SHT(DRV_NAME),
+ /** These controllers support Large Block Transfer which allows
+ transfer chunks up to 2GB and which cross 64KB boundaries,
+ therefore the DMA limits are more relaxed than standard ATA SFF. */
+ .dma_boundary = SIL_DMA_BOUNDARY,
+ .sg_tablesize = ATA_MAX_PRD
};
static struct ata_port_operations sil_ops = {
.inherits = &ata_bmdma_port_ops,
.dev_config = sil_dev_config,
.set_mode = sil_set_mode,
+ .bmdma_setup = sil_bmdma_setup,
+ .bmdma_start = sil_bmdma_start,
+ .bmdma_stop = sil_bmdma_stop,
+ .qc_prep = sil_qc_prep,
.freeze = sil_freeze,
.thaw = sil_thaw,
.scr_read = sil_scr_read,
@@ -249,6 +264,83 @@ module_param(slow_down, int, 0444);
MODULE_PARM_DESC(slow_down, "Sledgehammer used to work around random problems, by limiting commands to 15 sectors (0=off, 1=on)");
+static void sil_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ void __iomem *mmio_base = ap->host->iomap[SIL_MMIO_BAR];
+ void __iomem *bmdma2 = mmio_base + sil_port[ap->port_no].bmdma2;
+
+ /* clear start/stop bit - can safely always write 0 */
+ iowrite8(0, bmdma2);
+
+ /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */
+ ata_sff_dma_pause(ap);
+}
+
+static void sil_bmdma_setup(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ void __iomem *bmdma = ap->ioaddr.bmdma_addr;
+
+ /* load PRD table addr. */
+ iowrite32(ap->prd_dma, bmdma + ATA_DMA_TABLE_OFS);
+
+ /* issue r/w command */
+ ap->ops->sff_exec_command(ap, &qc->tf);
+}
+
+static void sil_bmdma_start(struct ata_queued_cmd *qc)
+{
+ unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE);
+ struct ata_port *ap = qc->ap;
+ void __iomem *mmio_base = ap->host->iomap[SIL_MMIO_BAR];
+ void __iomem *bmdma2 = mmio_base + sil_port[ap->port_no].bmdma2;
+ u8 dmactl = ATA_DMA_START;
+
+ /* set transfer direction, start host DMA transaction
+ Note: For Large Block Transfer to work, the DMA must be started
+ using the bmdma2 register. */
+ if (!rw)
+ dmactl |= ATA_DMA_WR;
+ iowrite8(dmactl, bmdma2);
+}
+
+/* The way God intended PCI IDE scatter/gather lists to look and behave... */
+static void sil_fill_sg(struct ata_queued_cmd *qc)
+{
+ struct scatterlist *sg;
+ struct ata_port *ap = qc->ap;
+ struct ata_prd *prd, *last_prd = NULL;
+ unsigned int si;
+
+ prd = &ap->prd[0];
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
+ /* Note h/w doesn't support 64-bit, so we unconditionally
+ * truncate dma_addr_t to u32.
+ */
+ u32 addr = (u32) sg_dma_address(sg);
+ u32 sg_len = sg_dma_len(sg);
+
+ prd->addr = cpu_to_le32(addr);
+ prd->flags_len = cpu_to_le32(sg_len);
+ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", pi, addr, sg_len);
+
+ last_prd = prd;
+ prd++;
+ }
+
+ if (likely(last_prd))
+ last_prd->flags_len |= cpu_to_le32(ATA_PRD_EOT);
+}
+
+static void sil_qc_prep(struct ata_queued_cmd *qc)
+{
+ if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+ return;
+
+ sil_fill_sg(qc);
+}
+
static unsigned char sil_get_device_cache_line(struct pci_dev *pdev)
{
u8 cache_line = 0;
@@ -278,7 +370,7 @@ static int sil_set_mode(struct ata_link *link, struct ata_device **r_failed)
if (rc)
return rc;
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
if (!ata_dev_enabled(dev))
dev_mode[dev->devno] = 0; /* PIO0/1/2 */
else if (dev->flags & ATA_DFLAG_PIO)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 9f7c543cc04b..01e69383d9c0 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -164,7 +164,7 @@ static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo);
static int cciss_revalidate(struct gendisk *disk);
static int rebuild_lun_table(ctlr_info_t *h, int first_time);
-static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
+static int deregister_disk(ctlr_info_t *h, int drv_index,
int clear_all);
static void cciss_read_capacity(int ctlr, int logvol, int withirq,
@@ -215,31 +215,17 @@ static struct block_device_operations cciss_fops = {
/*
* Enqueuing and dequeuing functions for cmdlists.
*/
-static inline void addQ(CommandList_struct **Qptr, CommandList_struct *c)
+static inline void addQ(struct hlist_head *list, CommandList_struct *c)
{
- if (*Qptr == NULL) {
- *Qptr = c;
- c->next = c->prev = c;
- } else {
- c->prev = (*Qptr)->prev;
- c->next = (*Qptr);
- (*Qptr)->prev->next = c;
- (*Qptr)->prev = c;
- }
+ hlist_add_head(&c->list, list);
}
-static inline CommandList_struct *removeQ(CommandList_struct **Qptr,
- CommandList_struct *c)
+static inline void removeQ(CommandList_struct *c)
{
- if (c && c->next != c) {
- if (*Qptr == c)
- *Qptr = c->next;
- c->prev->next = c->next;
- c->next->prev = c->prev;
- } else {
- *Qptr = NULL;
- }
- return c;
+ if (WARN_ON(hlist_unhashed(&c->list)))
+ return;
+
+ hlist_del_init(&c->list);
}
#include "cciss_scsi.c" /* For SCSI tape support */
@@ -506,6 +492,7 @@ static CommandList_struct *cmd_alloc(ctlr_info_t *h, int get_from_pool)
c->cmdindex = i;
}
+ INIT_HLIST_NODE(&c->list);
c->busaddr = (__u32) cmd_dma_handle;
temp64.val = (__u64) err_dma_handle;
c->ErrDesc.Addr.lower = temp64.val32.lower;
@@ -1492,8 +1479,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
* which keeps the interrupt handler from starting
* the queue.
*/
- ret = deregister_disk(h->gendisk[drv_index],
- &h->drv[drv_index], 0);
+ ret = deregister_disk(h, drv_index, 0);
h->drv[drv_index].busy_configuring = 0;
}
@@ -1711,8 +1697,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
h->drv[i].busy_configuring = 1;
spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
- return_code = deregister_disk(h->gendisk[i],
- &h->drv[i], 1);
+ return_code = deregister_disk(h, i, 1);
h->drv[i].busy_configuring = 0;
}
}
@@ -1782,15 +1767,19 @@ mem_msg:
* the highest_lun should be left unchanged and the LunID
* should not be cleared.
*/
-static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
+static int deregister_disk(ctlr_info_t *h, int drv_index,
int clear_all)
{
int i;
- ctlr_info_t *h = get_host(disk);
+ struct gendisk *disk;
+ drive_info_struct *drv;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
+ drv = &h->drv[drv_index];
+ disk = h->gendisk[drv_index];
+
/* make sure logical volume is NOT is use */
if (clear_all || (h->gendisk[0] == disk)) {
if (drv->usage_count > 1)
@@ -2548,7 +2537,8 @@ static void start_io(ctlr_info_t *h)
{
CommandList_struct *c;
- while ((c = h->reqQ) != NULL) {
+ while (!hlist_empty(&h->reqQ)) {
+ c = hlist_entry(h->reqQ.first, CommandList_struct, list);
/* can't do anything if fifo is full */
if ((h->access.fifo_full(h))) {
printk(KERN_WARNING "cciss: fifo full\n");
@@ -2556,14 +2546,14 @@ static void start_io(ctlr_info_t *h)
}
/* Get the first entry from the Request Q */
- removeQ(&(h->reqQ), c);
+ removeQ(c);
h->Qdepth--;
/* Tell the controller execute command */
h->access.submit_command(h, c);
/* Put job onto the completed Q */
- addQ(&(h->cmpQ), c);
+ addQ(&h->cmpQ, c);
}
}
@@ -2576,7 +2566,7 @@ static inline void resend_cciss_cmd(ctlr_info_t *h, CommandList_struct *c)
memset(c->err_info, 0, sizeof(ErrorInfo_struct));
/* add it to software queue and then send it to the controller */
- addQ(&(h->reqQ), c);
+ addQ(&h->reqQ, c);
h->Qdepth++;
if (h->Qdepth > h->maxQsinceinit)
h->maxQsinceinit = h->Qdepth;
@@ -2897,7 +2887,7 @@ static void do_cciss_request(struct request_queue *q)
spin_lock_irq(q->queue_lock);
- addQ(&(h->reqQ), c);
+ addQ(&h->reqQ, c);
h->Qdepth++;
if (h->Qdepth > h->maxQsinceinit)
h->maxQsinceinit = h->Qdepth;
@@ -2985,16 +2975,12 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
a = c->busaddr;
} else {
+ struct hlist_node *tmp;
+
a &= ~3;
- if ((c = h->cmpQ) == NULL) {
- printk(KERN_WARNING
- "cciss: Completion of %08x ignored\n",
- a1);
- continue;
- }
- while (c->busaddr != a) {
- c = c->next;
- if (c == h->cmpQ)
+ c = NULL;
+ hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
+ if (c->busaddr == a)
break;
}
}
@@ -3002,8 +2988,8 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
* If we've found the command, take it off the
* completion Q and free it
*/
- if (c->busaddr == a) {
- removeQ(&h->cmpQ, c);
+ if (c && c->busaddr == a) {
+ removeQ(c);
if (c->cmd_type == CMD_RWREQ) {
complete_command(h, c, 0);
} else if (c->cmd_type == CMD_IOCTL_PEND) {
@@ -3423,6 +3409,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
return -1;
hba[i]->busy_initializing = 1;
+ INIT_HLIST_HEAD(&hba[i]->cmpQ);
+ INIT_HLIST_HEAD(&hba[i]->reqQ);
if (cciss_pci_init(hba[i], pdev) != 0)
goto clean1;
@@ -3730,15 +3718,17 @@ static void fail_all_cmds(unsigned long ctlr)
pci_disable_device(h->pdev); /* Make sure it is really dead. */
/* move everything off the request queue onto the completed queue */
- while ((c = h->reqQ) != NULL) {
- removeQ(&(h->reqQ), c);
+ while (!hlist_empty(&h->reqQ)) {
+ c = hlist_entry(h->reqQ.first, CommandList_struct, list);
+ removeQ(c);
h->Qdepth--;
- addQ(&(h->cmpQ), c);
+ addQ(&h->cmpQ, c);
}
/* Now, fail everything on the completed queue with a HW error */
- while ((c = h->cmpQ) != NULL) {
- removeQ(&h->cmpQ, c);
+ while (!hlist_empty(&h->cmpQ)) {
+ c = hlist_entry(h->cmpQ.first, CommandList_struct, list);
+ removeQ(c);
c->err_info->CommandStatus = CMD_HARDWARE_ERR;
if (c->cmd_type == CMD_RWREQ) {
complete_command(h, c, 0);
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index 24a7efa993ab..15e2b84734e3 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -89,8 +89,8 @@ struct ctlr_info
struct access_method access;
/* queue and queue Info */
- CommandList_struct *reqQ;
- CommandList_struct *cmpQ;
+ struct hlist_head reqQ;
+ struct hlist_head cmpQ;
unsigned int Qdepth;
unsigned int maxQsinceinit;
unsigned int maxSG;
diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h
index 43bf5593b59b..24e22dea1a99 100644
--- a/drivers/block/cciss_cmd.h
+++ b/drivers/block/cciss_cmd.h
@@ -265,8 +265,7 @@ typedef struct _CommandList_struct {
int ctlr;
int cmd_type;
long cmdindex;
- struct _CommandList_struct *prev;
- struct _CommandList_struct *next;
+ struct hlist_node list;
struct request * rq;
struct completion *waiting;
int retry_count;
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index fb06ed659212..edbaac6c0573 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -624,20 +624,38 @@ static int loop_switch(struct loop_device *lo, struct file *file)
}
/*
+ * Helper to flush the IOs in loop, but keeping loop thread running
+ */
+static int loop_flush(struct loop_device *lo)
+{
+ /* loop not yet configured, no running thread, nothing to flush */
+ if (!lo->lo_thread)
+ return 0;
+
+ return loop_switch(lo, NULL);
+}
+
+/*
* Do the actual switch; called from the BIO completion routine
*/
static void do_loop_switch(struct loop_device *lo, struct switch_request *p)
{
struct file *file = p->file;
struct file *old_file = lo->lo_backing_file;
- struct address_space *mapping = file->f_mapping;
+ struct address_space *mapping;
+
+ /* if no new file, only flush of queued bios requested */
+ if (!file)
+ goto out;
+ mapping = file->f_mapping;
mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask);
lo->lo_backing_file = file;
lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ?
mapping->host->i_bdev->bd_block_size : PAGE_SIZE;
lo->old_gfp_mask = mapping_gfp_mask(mapping);
mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
+out:
complete(&p->wait);
}
@@ -901,6 +919,7 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
kthread_stop(lo->lo_thread);
+ lo->lo_queue->unplug_fn = NULL;
lo->lo_backing_file = NULL;
loop_release_xfer(lo);
@@ -1345,11 +1364,25 @@ static int lo_release(struct gendisk *disk, fmode_t mode)
struct loop_device *lo = disk->private_data;
mutex_lock(&lo->lo_ctl_mutex);
- --lo->lo_refcnt;
- if ((lo->lo_flags & LO_FLAGS_AUTOCLEAR) && !lo->lo_refcnt)
+ if (--lo->lo_refcnt)
+ goto out;
+
+ if (lo->lo_flags & LO_FLAGS_AUTOCLEAR) {
+ /*
+ * In autoclear mode, stop the loop thread
+ * and remove configuration after last close.
+ */
loop_clr_fd(lo, NULL);
+ } else {
+ /*
+ * Otherwise keep thread (if running) and config,
+ * but flush possible ongoing bios in thread.
+ */
+ loop_flush(lo);
+ }
+out:
mutex_unlock(&lo->lo_ctl_mutex);
return 0;
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index d3a91cacee8c..7bcc1d8bc967 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -722,7 +722,6 @@ static int __init nbd_init(void)
for (i = 0; i < nbds_max; i++) {
struct gendisk *disk = alloc_disk(1 << part_shift);
- elevator_t *old_e;
if (!disk)
goto out;
nbd_dev[i].disk = disk;
@@ -736,11 +735,10 @@ static int __init nbd_init(void)
put_disk(disk);
goto out;
}
- old_e = disk->queue->elevator;
- if (elevator_init(disk->queue, "deadline") == 0 ||
- elevator_init(disk->queue, "noop") == 0) {
- elevator_exit(old_e);
- }
+ /*
+ * Tell the block layer that we are not a rotational device
+ */
+ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, disk->queue);
}
if (register_blkdev(NBD_MAJOR, "nbd")) {
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 85d79a02d487..5d34764c8a87 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -6,7 +6,6 @@
#include <linux/virtio_blk.h>
#include <linux/scatterlist.h>
-#define VIRTIO_MAX_SG (3+MAX_PHYS_SEGMENTS)
#define PART_BITS 4
static int major, index;
@@ -26,8 +25,11 @@ struct virtio_blk
mempool_t *pool;
+ /* What host tells us, plus 2 for header & tailer. */
+ unsigned int sg_elems;
+
/* Scatterlist: can be too big for stack. */
- struct scatterlist sg[VIRTIO_MAX_SG];
+ struct scatterlist sg[/*sg_elems*/];
};
struct virtblk_req
@@ -97,8 +99,6 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
if (blk_barrier_rq(vbr->req))
vbr->out_hdr.type |= VIRTIO_BLK_T_BARRIER;
- /* This init could be done at vblk creation time */
- sg_init_table(vblk->sg, VIRTIO_MAX_SG);
sg_set_buf(&vblk->sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr));
num = blk_rq_map_sg(q, vbr->req, vblk->sg+1);
sg_set_buf(&vblk->sg[num+1], &vbr->status, sizeof(vbr->status));
@@ -130,7 +130,7 @@ static void do_virtblk_request(struct request_queue *q)
while ((req = elv_next_request(q)) != NULL) {
vblk = req->rq_disk->private_data;
- BUG_ON(req->nr_phys_segments > ARRAY_SIZE(vblk->sg));
+ BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems);
/* If this request fails, stop queue and wait for something to
finish to restart it. */
@@ -196,12 +196,22 @@ static int virtblk_probe(struct virtio_device *vdev)
int err;
u64 cap;
u32 v;
- u32 blk_size;
+ u32 blk_size, sg_elems;
if (index_to_minor(index) >= 1 << MINORBITS)
return -ENOSPC;
- vdev->priv = vblk = kmalloc(sizeof(*vblk), GFP_KERNEL);
+ /* We need to know how many segments before we allocate. */
+ err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX,
+ offsetof(struct virtio_blk_config, seg_max),
+ &sg_elems);
+ if (err)
+ sg_elems = 1;
+
+ /* We need an extra sg elements at head and tail. */
+ sg_elems += 2;
+ vdev->priv = vblk = kmalloc(sizeof(*vblk) +
+ sizeof(vblk->sg[0]) * sg_elems, GFP_KERNEL);
if (!vblk) {
err = -ENOMEM;
goto out;
@@ -210,6 +220,8 @@ static int virtblk_probe(struct virtio_device *vdev)
INIT_LIST_HEAD(&vblk->reqs);
spin_lock_init(&vblk->lock);
vblk->vdev = vdev;
+ vblk->sg_elems = sg_elems;
+ sg_init_table(vblk->sg, vblk->sg_elems);
/* We expect one virtqueue, for output. */
vblk->vq = vdev->config->find_vq(vdev, 0, blk_done);
@@ -237,6 +249,8 @@ static int virtblk_probe(struct virtio_device *vdev)
goto out_put_disk;
}
+ queue_flag_set_unlocked(QUEUE_FLAG_VIRT, vblk->disk->queue);
+
if (index < 26) {
sprintf(vblk->disk->disk_name, "vd%c", 'a' + index % 26);
} else if (index < (26 + 1) * 26) {
@@ -277,6 +291,13 @@ static int virtblk_probe(struct virtio_device *vdev)
}
set_capacity(vblk->disk, cap);
+ /* We can handle whatever the host told us to handle. */
+ blk_queue_max_phys_segments(vblk->disk->queue, vblk->sg_elems-2);
+ blk_queue_max_hw_segments(vblk->disk->queue, vblk->sg_elems-2);
+
+ /* No real sector limit. */
+ blk_queue_max_sectors(vblk->disk->queue, -1U);
+
/* Host can optionally specify maximum segment size and number of
* segments. */
err = virtio_config_val(vdev, VIRTIO_BLK_F_SIZE_MAX,
@@ -284,12 +305,8 @@ static int virtblk_probe(struct virtio_device *vdev)
&v);
if (!err)
blk_queue_max_segment_size(vblk->disk->queue, v);
-
- err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX,
- offsetof(struct virtio_blk_config, seg_max),
- &v);
- if (!err)
- blk_queue_max_hw_segments(vblk->disk->queue, v);
+ else
+ blk_queue_max_segment_size(vblk->disk->queue, -1U);
/* Host can optionally specify the block size of the device */
err = virtio_config_val(vdev, VIRTIO_BLK_F_BLK_SIZE,
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 2d19f0cc47f2..918ef725de41 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -338,18 +338,12 @@ wait:
static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
{
struct request_queue *rq;
- elevator_t *old_e;
rq = blk_init_queue(do_blkif_request, &blkif_io_lock);
if (rq == NULL)
return -1;
- old_e = rq->elevator;
- if (IS_ERR_VALUE(elevator_init(rq, "noop")))
- printk(KERN_WARNING
- "blkfront: Switch elevator failed, use default\n");
- else
- elevator_exit(old_e);
+ queue_flag_set_unlocked(QUEUE_FLAG_VIRT, rq);
/* Hard sector size and max sectors impersonate the equiv. hardware. */
blk_queue_hardsect_size(rq, sector_size);
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 7d2e91cccb13..cceace61ef28 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -1712,29 +1712,30 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai)
return 0;
}
-static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s)
+static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s,
+ struct packet_command *cgc)
{
unsigned char buf[21], *base;
struct dvd_layer *layer;
- struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
int ret, layer_num = s->physical.layer_num;
if (layer_num >= DVD_LAYERS)
return -EINVAL;
- init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
- cgc.cmd[6] = layer_num;
- cgc.cmd[7] = s->type;
- cgc.cmd[9] = cgc.buflen & 0xff;
+ init_cdrom_command(cgc, buf, sizeof(buf), CGC_DATA_READ);
+ cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc->cmd[6] = layer_num;
+ cgc->cmd[7] = s->type;
+ cgc->cmd[9] = cgc->buflen & 0xff;
/*
* refrain from reporting errors on non-existing layers (mainly)
*/
- cgc.quiet = 1;
+ cgc->quiet = 1;
- if ((ret = cdo->generic_packet(cdi, &cgc)))
+ ret = cdo->generic_packet(cdi, cgc);
+ if (ret)
return ret;
base = &buf[4];
@@ -1762,21 +1763,22 @@ static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s)
return 0;
}
-static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s)
+static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s,
+ struct packet_command *cgc)
{
int ret;
u_char buf[8];
- struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
- init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
- cgc.cmd[6] = s->copyright.layer_num;
- cgc.cmd[7] = s->type;
- cgc.cmd[8] = cgc.buflen >> 8;
- cgc.cmd[9] = cgc.buflen & 0xff;
+ init_cdrom_command(cgc, buf, sizeof(buf), CGC_DATA_READ);
+ cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc->cmd[6] = s->copyright.layer_num;
+ cgc->cmd[7] = s->type;
+ cgc->cmd[8] = cgc->buflen >> 8;
+ cgc->cmd[9] = cgc->buflen & 0xff;
- if ((ret = cdo->generic_packet(cdi, &cgc)))
+ ret = cdo->generic_packet(cdi, cgc);
+ if (ret)
return ret;
s->copyright.cpst = buf[4];
@@ -1785,79 +1787,89 @@ static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s)
return 0;
}
-static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s)
+static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s,
+ struct packet_command *cgc)
{
int ret, size;
u_char *buf;
- struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
size = sizeof(s->disckey.value) + 4;
- if ((buf = kmalloc(size, GFP_KERNEL)) == NULL)
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
return -ENOMEM;
- init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
- cgc.cmd[7] = s->type;
- cgc.cmd[8] = size >> 8;
- cgc.cmd[9] = size & 0xff;
- cgc.cmd[10] = s->disckey.agid << 6;
+ init_cdrom_command(cgc, buf, size, CGC_DATA_READ);
+ cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc->cmd[7] = s->type;
+ cgc->cmd[8] = size >> 8;
+ cgc->cmd[9] = size & 0xff;
+ cgc->cmd[10] = s->disckey.agid << 6;
- if (!(ret = cdo->generic_packet(cdi, &cgc)))
+ ret = cdo->generic_packet(cdi, cgc);
+ if (!ret)
memcpy(s->disckey.value, &buf[4], sizeof(s->disckey.value));
kfree(buf);
return ret;
}
-static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s)
+static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s,
+ struct packet_command *cgc)
{
- int ret;
- u_char buf[4 + 188];
- struct packet_command cgc;
+ int ret, size = 4 + 188;
+ u_char *buf;
struct cdrom_device_ops *cdo = cdi->ops;
- init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
- cgc.cmd[7] = s->type;
- cgc.cmd[9] = cgc.buflen & 0xff;
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
- if ((ret = cdo->generic_packet(cdi, &cgc)))
- return ret;
+ init_cdrom_command(cgc, buf, size, CGC_DATA_READ);
+ cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc->cmd[7] = s->type;
+ cgc->cmd[9] = cgc->buflen & 0xff;
+
+ ret = cdo->generic_packet(cdi, cgc);
+ if (ret)
+ goto out;
s->bca.len = buf[0] << 8 | buf[1];
if (s->bca.len < 12 || s->bca.len > 188) {
cdinfo(CD_WARNING, "Received invalid BCA length (%d)\n", s->bca.len);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
memcpy(s->bca.value, &buf[4], s->bca.len);
-
- return 0;
+ ret = 0;
+out:
+ kfree(buf);
+ return ret;
}
-static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s)
+static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s,
+ struct packet_command *cgc)
{
int ret = 0, size;
u_char *buf;
- struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
size = sizeof(s->manufact.value) + 4;
- if ((buf = kmalloc(size, GFP_KERNEL)) == NULL)
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
return -ENOMEM;
- init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
- cgc.cmd[7] = s->type;
- cgc.cmd[8] = size >> 8;
- cgc.cmd[9] = size & 0xff;
+ init_cdrom_command(cgc, buf, size, CGC_DATA_READ);
+ cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc->cmd[7] = s->type;
+ cgc->cmd[8] = size >> 8;
+ cgc->cmd[9] = size & 0xff;
- if ((ret = cdo->generic_packet(cdi, &cgc))) {
- kfree(buf);
- return ret;
- }
+ ret = cdo->generic_packet(cdi, cgc);
+ if (ret)
+ goto out;
s->manufact.len = buf[0] << 8 | buf[1];
if (s->manufact.len < 0 || s->manufact.len > 2048) {
@@ -1868,27 +1880,29 @@ static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s)
memcpy(s->manufact.value, &buf[4], s->manufact.len);
}
+out:
kfree(buf);
return ret;
}
-static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s)
+static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s,
+ struct packet_command *cgc)
{
switch (s->type) {
case DVD_STRUCT_PHYSICAL:
- return dvd_read_physical(cdi, s);
+ return dvd_read_physical(cdi, s, cgc);
case DVD_STRUCT_COPYRIGHT:
- return dvd_read_copyright(cdi, s);
+ return dvd_read_copyright(cdi, s, cgc);
case DVD_STRUCT_DISCKEY:
- return dvd_read_disckey(cdi, s);
+ return dvd_read_disckey(cdi, s, cgc);
case DVD_STRUCT_BCA:
- return dvd_read_bca(cdi, s);
+ return dvd_read_bca(cdi, s, cgc);
case DVD_STRUCT_MANUFACT:
- return dvd_read_manufact(cdi, s);
+ return dvd_read_manufact(cdi, s, cgc);
default:
cdinfo(CD_WARNING, ": Invalid DVD structure read requested (%d)\n",
@@ -2787,271 +2801,360 @@ static int cdrom_switch_blocksize(struct cdrom_device_info *cdi, int size)
return cdo->generic_packet(cdi, &cgc);
}
-static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
- unsigned long arg)
-{
- struct cdrom_device_ops *cdo = cdi->ops;
- struct packet_command cgc;
+static noinline int mmc_ioctl_cdrom_read_data(struct cdrom_device_info *cdi,
+ void __user *arg,
+ struct packet_command *cgc,
+ int cmd)
+{
struct request_sense sense;
- unsigned char buffer[32];
- int ret = 0;
-
- memset(&cgc, 0, sizeof(cgc));
+ struct cdrom_msf msf;
+ int blocksize = 0, format = 0, lba;
+ int ret;
- /* build a unified command and queue it through
- cdo->generic_packet() */
switch (cmd) {
case CDROMREADRAW:
+ blocksize = CD_FRAMESIZE_RAW;
+ break;
case CDROMREADMODE1:
- case CDROMREADMODE2: {
- struct cdrom_msf msf;
- int blocksize = 0, format = 0, lba;
-
- switch (cmd) {
- case CDROMREADRAW:
- blocksize = CD_FRAMESIZE_RAW;
- break;
- case CDROMREADMODE1:
- blocksize = CD_FRAMESIZE;
- format = 2;
- break;
- case CDROMREADMODE2:
- blocksize = CD_FRAMESIZE_RAW0;
- break;
- }
- IOCTL_IN(arg, struct cdrom_msf, msf);
- lba = msf_to_lba(msf.cdmsf_min0,msf.cdmsf_sec0,msf.cdmsf_frame0);
- /* FIXME: we need upper bound checking, too!! */
- if (lba < 0)
- return -EINVAL;
- cgc.buffer = kmalloc(blocksize, GFP_KERNEL);
- if (cgc.buffer == NULL)
- return -ENOMEM;
- memset(&sense, 0, sizeof(sense));
- cgc.sense = &sense;
- cgc.data_direction = CGC_DATA_READ;
- ret = cdrom_read_block(cdi, &cgc, lba, 1, format, blocksize);
- if (ret && sense.sense_key==0x05 && sense.asc==0x20 && sense.ascq==0x00) {
- /*
- * SCSI-II devices are not required to support
- * READ_CD, so let's try switching block size
- */
- /* FIXME: switch back again... */
- if ((ret = cdrom_switch_blocksize(cdi, blocksize))) {
- kfree(cgc.buffer);
- return ret;
- }
- cgc.sense = NULL;
- ret = cdrom_read_cd(cdi, &cgc, lba, blocksize, 1);
- ret |= cdrom_switch_blocksize(cdi, blocksize);
- }
- if (!ret && copy_to_user((char __user *)arg, cgc.buffer, blocksize))
- ret = -EFAULT;
- kfree(cgc.buffer);
+ blocksize = CD_FRAMESIZE;
+ format = 2;
+ break;
+ case CDROMREADMODE2:
+ blocksize = CD_FRAMESIZE_RAW0;
+ break;
+ }
+ IOCTL_IN(arg, struct cdrom_msf, msf);
+ lba = msf_to_lba(msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0);
+ /* FIXME: we need upper bound checking, too!! */
+ if (lba < 0)
+ return -EINVAL;
+
+ cgc->buffer = kmalloc(blocksize, GFP_KERNEL);
+ if (cgc->buffer == NULL)
+ return -ENOMEM;
+
+ memset(&sense, 0, sizeof(sense));
+ cgc->sense = &sense;
+ cgc->data_direction = CGC_DATA_READ;
+ ret = cdrom_read_block(cdi, cgc, lba, 1, format, blocksize);
+ if (ret && sense.sense_key == 0x05 &&
+ sense.asc == 0x20 &&
+ sense.ascq == 0x00) {
+ /*
+ * SCSI-II devices are not required to support
+ * READ_CD, so let's try switching block size
+ */
+ /* FIXME: switch back again... */
+ ret = cdrom_switch_blocksize(cdi, blocksize);
+ if (ret)
+ goto out;
+ cgc->sense = NULL;
+ ret = cdrom_read_cd(cdi, cgc, lba, blocksize, 1);
+ ret |= cdrom_switch_blocksize(cdi, blocksize);
+ }
+ if (!ret && copy_to_user(arg, cgc->buffer, blocksize))
+ ret = -EFAULT;
+out:
+ kfree(cgc->buffer);
+ return ret;
+}
+
+static noinline int mmc_ioctl_cdrom_read_audio(struct cdrom_device_info *cdi,
+ void __user *arg)
+{
+ struct cdrom_read_audio ra;
+ int lba;
+
+ IOCTL_IN(arg, struct cdrom_read_audio, ra);
+
+ if (ra.addr_format == CDROM_MSF)
+ lba = msf_to_lba(ra.addr.msf.minute,
+ ra.addr.msf.second,
+ ra.addr.msf.frame);
+ else if (ra.addr_format == CDROM_LBA)
+ lba = ra.addr.lba;
+ else
+ return -EINVAL;
+
+ /* FIXME: we need upper bound checking, too!! */
+ if (lba < 0 || ra.nframes <= 0 || ra.nframes > CD_FRAMES)
+ return -EINVAL;
+
+ return cdrom_read_cdda(cdi, ra.buf, lba, ra.nframes);
+}
+
+static noinline int mmc_ioctl_cdrom_subchannel(struct cdrom_device_info *cdi,
+ void __user *arg)
+{
+ int ret;
+ struct cdrom_subchnl q;
+ u_char requested, back;
+ IOCTL_IN(arg, struct cdrom_subchnl, q);
+ requested = q.cdsc_format;
+ if (!((requested == CDROM_MSF) ||
+ (requested == CDROM_LBA)))
+ return -EINVAL;
+ q.cdsc_format = CDROM_MSF;
+ ret = cdrom_read_subchannel(cdi, &q, 0);
+ if (ret)
return ret;
- }
- case CDROMREADAUDIO: {
- struct cdrom_read_audio ra;
- int lba;
-
- IOCTL_IN(arg, struct cdrom_read_audio, ra);
-
- if (ra.addr_format == CDROM_MSF)
- lba = msf_to_lba(ra.addr.msf.minute,
- ra.addr.msf.second,
- ra.addr.msf.frame);
- else if (ra.addr_format == CDROM_LBA)
- lba = ra.addr.lba;
- else
- return -EINVAL;
+ back = q.cdsc_format; /* local copy */
+ sanitize_format(&q.cdsc_absaddr, &back, requested);
+ sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
+ IOCTL_OUT(arg, struct cdrom_subchnl, q);
+ /* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */
+ return 0;
+}
- /* FIXME: we need upper bound checking, too!! */
- if (lba < 0 || ra.nframes <= 0 || ra.nframes > CD_FRAMES)
- return -EINVAL;
+static noinline int mmc_ioctl_cdrom_play_msf(struct cdrom_device_info *cdi,
+ void __user *arg,
+ struct packet_command *cgc)
+{
+ struct cdrom_device_ops *cdo = cdi->ops;
+ struct cdrom_msf msf;
+ cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n");
+ IOCTL_IN(arg, struct cdrom_msf, msf);
+ cgc->cmd[0] = GPCMD_PLAY_AUDIO_MSF;
+ cgc->cmd[3] = msf.cdmsf_min0;
+ cgc->cmd[4] = msf.cdmsf_sec0;
+ cgc->cmd[5] = msf.cdmsf_frame0;
+ cgc->cmd[6] = msf.cdmsf_min1;
+ cgc->cmd[7] = msf.cdmsf_sec1;
+ cgc->cmd[8] = msf.cdmsf_frame1;
+ cgc->data_direction = CGC_DATA_NONE;
+ return cdo->generic_packet(cdi, cgc);
+}
- return cdrom_read_cdda(cdi, ra.buf, lba, ra.nframes);
- }
- case CDROMSUBCHNL: {
- struct cdrom_subchnl q;
- u_char requested, back;
- IOCTL_IN(arg, struct cdrom_subchnl, q);
- requested = q.cdsc_format;
- if (!((requested == CDROM_MSF) ||
- (requested == CDROM_LBA)))
- return -EINVAL;
- q.cdsc_format = CDROM_MSF;
- if ((ret = cdrom_read_subchannel(cdi, &q, 0)))
- return ret;
- back = q.cdsc_format; /* local copy */
- sanitize_format(&q.cdsc_absaddr, &back, requested);
- sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
- IOCTL_OUT(arg, struct cdrom_subchnl, q);
- /* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */
- return 0;
- }
- case CDROMPLAYMSF: {
- struct cdrom_msf msf;
- cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n");
- IOCTL_IN(arg, struct cdrom_msf, msf);
- cgc.cmd[0] = GPCMD_PLAY_AUDIO_MSF;
- cgc.cmd[3] = msf.cdmsf_min0;
- cgc.cmd[4] = msf.cdmsf_sec0;
- cgc.cmd[5] = msf.cdmsf_frame0;
- cgc.cmd[6] = msf.cdmsf_min1;
- cgc.cmd[7] = msf.cdmsf_sec1;
- cgc.cmd[8] = msf.cdmsf_frame1;
- cgc.data_direction = CGC_DATA_NONE;
- return cdo->generic_packet(cdi, &cgc);
- }
- case CDROMPLAYBLK: {
- struct cdrom_blk blk;
- cdinfo(CD_DO_IOCTL, "entering CDROMPLAYBLK\n");
- IOCTL_IN(arg, struct cdrom_blk, blk);
- cgc.cmd[0] = GPCMD_PLAY_AUDIO_10;
- cgc.cmd[2] = (blk.from >> 24) & 0xff;
- cgc.cmd[3] = (blk.from >> 16) & 0xff;
- cgc.cmd[4] = (blk.from >> 8) & 0xff;
- cgc.cmd[5] = blk.from & 0xff;
- cgc.cmd[7] = (blk.len >> 8) & 0xff;
- cgc.cmd[8] = blk.len & 0xff;
- cgc.data_direction = CGC_DATA_NONE;
- return cdo->generic_packet(cdi, &cgc);
- }
- case CDROMVOLCTRL:
- case CDROMVOLREAD: {
- struct cdrom_volctrl volctrl;
- char mask[sizeof(buffer)];
- unsigned short offset;
+static noinline int mmc_ioctl_cdrom_play_blk(struct cdrom_device_info *cdi,
+ void __user *arg,
+ struct packet_command *cgc)
+{
+ struct cdrom_device_ops *cdo = cdi->ops;
+ struct cdrom_blk blk;
+ cdinfo(CD_DO_IOCTL, "entering CDROMPLAYBLK\n");
+ IOCTL_IN(arg, struct cdrom_blk, blk);
+ cgc->cmd[0] = GPCMD_PLAY_AUDIO_10;
+ cgc->cmd[2] = (blk.from >> 24) & 0xff;
+ cgc->cmd[3] = (blk.from >> 16) & 0xff;
+ cgc->cmd[4] = (blk.from >> 8) & 0xff;
+ cgc->cmd[5] = blk.from & 0xff;
+ cgc->cmd[7] = (blk.len >> 8) & 0xff;
+ cgc->cmd[8] = blk.len & 0xff;
+ cgc->data_direction = CGC_DATA_NONE;
+ return cdo->generic_packet(cdi, cgc);
+}
+
+static noinline int mmc_ioctl_cdrom_volume(struct cdrom_device_info *cdi,
+ void __user *arg,
+ struct packet_command *cgc,
+ unsigned int cmd)
+{
+ struct cdrom_volctrl volctrl;
+ unsigned char buffer[32];
+ char mask[sizeof(buffer)];
+ unsigned short offset;
+ int ret;
- cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n");
+ cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n");
- IOCTL_IN(arg, struct cdrom_volctrl, volctrl);
+ IOCTL_IN(arg, struct cdrom_volctrl, volctrl);
- cgc.buffer = buffer;
- cgc.buflen = 24;
- if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_AUDIO_CTL_PAGE, 0)))
- return ret;
+ cgc->buffer = buffer;
+ cgc->buflen = 24;
+ ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 0);
+ if (ret)
+ return ret;
- /* originally the code depended on buffer[1] to determine
- how much data is available for transfer. buffer[1] is
- unfortunately ambigious and the only reliable way seem
- to be to simply skip over the block descriptor... */
- offset = 8 + be16_to_cpu(*(__be16 *)(buffer+6));
-
- if (offset + 16 > sizeof(buffer))
- return -E2BIG;
-
- if (offset + 16 > cgc.buflen) {
- cgc.buflen = offset+16;
- ret = cdrom_mode_sense(cdi, &cgc,
- GPMODE_AUDIO_CTL_PAGE, 0);
- if (ret)
- return ret;
- }
+ /* originally the code depended on buffer[1] to determine
+ how much data is available for transfer. buffer[1] is
+ unfortunately ambigious and the only reliable way seem
+ to be to simply skip over the block descriptor... */
+ offset = 8 + be16_to_cpu(*(__be16 *)(buffer + 6));
+
+ if (offset + 16 > sizeof(buffer))
+ return -E2BIG;
+
+ if (offset + 16 > cgc->buflen) {
+ cgc->buflen = offset + 16;
+ ret = cdrom_mode_sense(cdi, cgc,
+ GPMODE_AUDIO_CTL_PAGE, 0);
+ if (ret)
+ return ret;
+ }
- /* sanity check */
- if ((buffer[offset] & 0x3f) != GPMODE_AUDIO_CTL_PAGE ||
- buffer[offset+1] < 14)
- return -EINVAL;
+ /* sanity check */
+ if ((buffer[offset] & 0x3f) != GPMODE_AUDIO_CTL_PAGE ||
+ buffer[offset + 1] < 14)
+ return -EINVAL;
- /* now we have the current volume settings. if it was only
- a CDROMVOLREAD, return these values */
- if (cmd == CDROMVOLREAD) {
- volctrl.channel0 = buffer[offset+9];
- volctrl.channel1 = buffer[offset+11];
- volctrl.channel2 = buffer[offset+13];
- volctrl.channel3 = buffer[offset+15];
- IOCTL_OUT(arg, struct cdrom_volctrl, volctrl);
- return 0;
- }
+ /* now we have the current volume settings. if it was only
+ a CDROMVOLREAD, return these values */
+ if (cmd == CDROMVOLREAD) {
+ volctrl.channel0 = buffer[offset+9];
+ volctrl.channel1 = buffer[offset+11];
+ volctrl.channel2 = buffer[offset+13];
+ volctrl.channel3 = buffer[offset+15];
+ IOCTL_OUT(arg, struct cdrom_volctrl, volctrl);
+ return 0;
+ }
- /* get the volume mask */
- cgc.buffer = mask;
- if ((ret = cdrom_mode_sense(cdi, &cgc,
- GPMODE_AUDIO_CTL_PAGE, 1)))
- return ret;
+ /* get the volume mask */
+ cgc->buffer = mask;
+ ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 1);
+ if (ret)
+ return ret;
- buffer[offset+9] = volctrl.channel0 & mask[offset+9];
- buffer[offset+11] = volctrl.channel1 & mask[offset+11];
- buffer[offset+13] = volctrl.channel2 & mask[offset+13];
- buffer[offset+15] = volctrl.channel3 & mask[offset+15];
+ buffer[offset + 9] = volctrl.channel0 & mask[offset + 9];
+ buffer[offset + 11] = volctrl.channel1 & mask[offset + 11];
+ buffer[offset + 13] = volctrl.channel2 & mask[offset + 13];
+ buffer[offset + 15] = volctrl.channel3 & mask[offset + 15];
- /* set volume */
- cgc.buffer = buffer + offset - 8;
- memset(cgc.buffer, 0, 8);
- return cdrom_mode_select(cdi, &cgc);
- }
+ /* set volume */
+ cgc->buffer = buffer + offset - 8;
+ memset(cgc->buffer, 0, 8);
+ return cdrom_mode_select(cdi, cgc);
+}
- case CDROMSTART:
- case CDROMSTOP: {
- cdinfo(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n");
- cgc.cmd[0] = GPCMD_START_STOP_UNIT;
- cgc.cmd[1] = 1;
- cgc.cmd[4] = (cmd == CDROMSTART) ? 1 : 0;
- cgc.data_direction = CGC_DATA_NONE;
- return cdo->generic_packet(cdi, &cgc);
- }
+static noinline int mmc_ioctl_cdrom_start_stop(struct cdrom_device_info *cdi,
+ struct packet_command *cgc,
+ int cmd)
+{
+ struct cdrom_device_ops *cdo = cdi->ops;
+ cdinfo(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n");
+ cgc->cmd[0] = GPCMD_START_STOP_UNIT;
+ cgc->cmd[1] = 1;
+ cgc->cmd[4] = (cmd == CDROMSTART) ? 1 : 0;
+ cgc->data_direction = CGC_DATA_NONE;
+ return cdo->generic_packet(cdi, cgc);
+}
- case CDROMPAUSE:
- case CDROMRESUME: {
- cdinfo(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n");
- cgc.cmd[0] = GPCMD_PAUSE_RESUME;
- cgc.cmd[8] = (cmd == CDROMRESUME) ? 1 : 0;
- cgc.data_direction = CGC_DATA_NONE;
- return cdo->generic_packet(cdi, &cgc);
- }
+static noinline int mmc_ioctl_cdrom_pause_resume(struct cdrom_device_info *cdi,
+ struct packet_command *cgc,
+ int cmd)
+{
+ struct cdrom_device_ops *cdo = cdi->ops;
+ cdinfo(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n");
+ cgc->cmd[0] = GPCMD_PAUSE_RESUME;
+ cgc->cmd[8] = (cmd == CDROMRESUME) ? 1 : 0;
+ cgc->data_direction = CGC_DATA_NONE;
+ return cdo->generic_packet(cdi, cgc);
+}
- case DVD_READ_STRUCT: {
- dvd_struct *s;
- int size = sizeof(dvd_struct);
- if (!CDROM_CAN(CDC_DVD))
- return -ENOSYS;
- 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)) {
- kfree(s);
- return -EFAULT;
- }
- if ((ret = dvd_read_struct(cdi, s))) {
- kfree(s);
- return ret;
- }
- if (copy_to_user((dvd_struct __user *)arg, s, size))
- ret = -EFAULT;
+static noinline int mmc_ioctl_dvd_read_struct(struct cdrom_device_info *cdi,
+ void __user *arg,
+ struct packet_command *cgc)
+{
+ int ret;
+ dvd_struct *s;
+ int size = sizeof(dvd_struct);
+
+ if (!CDROM_CAN(CDC_DVD))
+ return -ENOSYS;
+
+ s = kmalloc(size, GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
+
+ cdinfo(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n");
+ if (copy_from_user(s, arg, size)) {
kfree(s);
+ return -EFAULT;
+ }
+
+ ret = dvd_read_struct(cdi, s, cgc);
+ if (ret)
+ goto out;
+
+ if (copy_to_user(arg, s, size))
+ ret = -EFAULT;
+out:
+ kfree(s);
+ return ret;
+}
+
+static noinline int mmc_ioctl_dvd_auth(struct cdrom_device_info *cdi,
+ void __user *arg)
+{
+ int ret;
+ dvd_authinfo ai;
+ if (!CDROM_CAN(CDC_DVD))
+ return -ENOSYS;
+ cdinfo(CD_DO_IOCTL, "entering DVD_AUTH\n");
+ IOCTL_IN(arg, dvd_authinfo, ai);
+ ret = dvd_do_auth(cdi, &ai);
+ if (ret)
return ret;
- }
+ IOCTL_OUT(arg, dvd_authinfo, ai);
+ return 0;
+}
- case DVD_AUTH: {
- dvd_authinfo ai;
- if (!CDROM_CAN(CDC_DVD))
- return -ENOSYS;
- cdinfo(CD_DO_IOCTL, "entering DVD_AUTH\n");
- IOCTL_IN(arg, dvd_authinfo, ai);
- if ((ret = dvd_do_auth (cdi, &ai)))
- return ret;
- IOCTL_OUT(arg, dvd_authinfo, ai);
- return 0;
- }
+static noinline int mmc_ioctl_cdrom_next_writable(struct cdrom_device_info *cdi,
+ void __user *arg)
+{
+ int ret;
+ long next = 0;
+ cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n");
+ ret = cdrom_get_next_writable(cdi, &next);
+ if (ret)
+ return ret;
+ IOCTL_OUT(arg, long, next);
+ return 0;
+}
- case CDROM_NEXT_WRITABLE: {
- long next = 0;
- cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n");
- if ((ret = cdrom_get_next_writable(cdi, &next)))
- return ret;
- IOCTL_OUT(arg, long, next);
- return 0;
- }
- case CDROM_LAST_WRITTEN: {
- long last = 0;
- cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n");
- if ((ret = cdrom_get_last_written(cdi, &last)))
- return ret;
- IOCTL_OUT(arg, long, last);
- return 0;
- }
- } /* switch */
+static noinline int mmc_ioctl_cdrom_last_written(struct cdrom_device_info *cdi,
+ void __user *arg)
+{
+ int ret;
+ long last = 0;
+ cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n");
+ ret = cdrom_get_last_written(cdi, &last);
+ if (ret)
+ return ret;
+ IOCTL_OUT(arg, long, last);
+ return 0;
+}
+
+static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
+ unsigned long arg)
+{
+ struct packet_command cgc;
+ void __user *userptr = (void __user *)arg;
+
+ memset(&cgc, 0, sizeof(cgc));
+
+ /* build a unified command and queue it through
+ cdo->generic_packet() */
+ switch (cmd) {
+ case CDROMREADRAW:
+ case CDROMREADMODE1:
+ case CDROMREADMODE2:
+ return mmc_ioctl_cdrom_read_data(cdi, userptr, &cgc, cmd);
+ case CDROMREADAUDIO:
+ return mmc_ioctl_cdrom_read_audio(cdi, userptr);
+ case CDROMSUBCHNL:
+ return mmc_ioctl_cdrom_subchannel(cdi, userptr);
+ case CDROMPLAYMSF:
+ return mmc_ioctl_cdrom_play_msf(cdi, userptr, &cgc);
+ case CDROMPLAYBLK:
+ return mmc_ioctl_cdrom_play_blk(cdi, userptr, &cgc);
+ case CDROMVOLCTRL:
+ case CDROMVOLREAD:
+ return mmc_ioctl_cdrom_volume(cdi, userptr, &cgc, cmd);
+ case CDROMSTART:
+ case CDROMSTOP:
+ return mmc_ioctl_cdrom_start_stop(cdi, &cgc, cmd);
+ case CDROMPAUSE:
+ case CDROMRESUME:
+ return mmc_ioctl_cdrom_pause_resume(cdi, &cgc, cmd);
+ case DVD_READ_STRUCT:
+ return mmc_ioctl_dvd_read_struct(cdi, userptr, &cgc);
+ case DVD_AUTH:
+ return mmc_ioctl_dvd_auth(cdi, userptr);
+ case CDROM_NEXT_WRITABLE:
+ return mmc_ioctl_cdrom_next_writable(cdi, userptr);
+ case CDROM_LAST_WRITTEN:
+ return mmc_ioctl_cdrom_last_written(cdi, userptr);
+ }
return -ENOTTY;
}
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index 9cf6e9bb017e..c7714185f831 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -40,6 +40,8 @@
#define PCI_DEVICE_ID_INTEL_Q45_IG 0x2E12
#define PCI_DEVICE_ID_INTEL_G45_HB 0x2E20
#define PCI_DEVICE_ID_INTEL_G45_IG 0x2E22
+#define PCI_DEVICE_ID_INTEL_G41_HB 0x2E30
+#define PCI_DEVICE_ID_INTEL_G41_IG 0x2E32
/* cover 915 and 945 variants */
#define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \
@@ -63,7 +65,8 @@
#define IS_G4X (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGD_E_HB || \
agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q45_HB || \
agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB)
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB)
extern int agp_memory_reserved;
@@ -1196,6 +1199,7 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size)
case PCI_DEVICE_ID_INTEL_IGD_E_HB:
case PCI_DEVICE_ID_INTEL_Q45_HB:
case PCI_DEVICE_ID_INTEL_G45_HB:
+ case PCI_DEVICE_ID_INTEL_G41_HB:
*gtt_offset = *gtt_size = MB(2);
break;
default:
@@ -2156,13 +2160,15 @@ static const struct intel_driver_description {
{ PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, 0, "Q33",
NULL, &intel_g33_driver },
{ PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG, 0,
- "Mobile Intel? GM45 Express", NULL, &intel_i965_driver },
+ "Mobile Intel® GM45 Express", NULL, &intel_i965_driver },
{ PCI_DEVICE_ID_INTEL_IGD_E_HB, PCI_DEVICE_ID_INTEL_IGD_E_IG, 0,
"Intel Integrated Graphics Device", NULL, &intel_i965_driver },
{ PCI_DEVICE_ID_INTEL_Q45_HB, PCI_DEVICE_ID_INTEL_Q45_IG, 0,
"Q45/Q43", NULL, &intel_i965_driver },
{ PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG, 0,
"G45/G43", NULL, &intel_i965_driver },
+ { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, 0,
+ "G41", NULL, &intel_i965_driver },
{ 0, 0, 0, NULL, NULL, NULL }
};
@@ -2360,6 +2366,7 @@ static struct pci_device_id agp_intel_pci_table[] = {
ID(PCI_DEVICE_ID_INTEL_IGD_E_HB),
ID(PCI_DEVICE_ID_INTEL_Q45_HB),
ID(PCI_DEVICE_ID_INTEL_G45_HB),
+ ID(PCI_DEVICE_ID_INTEL_G41_HB),
{ }
};
diff --git a/drivers/char/ds1620.c b/drivers/char/ds1620.c
index 74e9cd81b5b2..61f0146e215d 100644
--- a/drivers/char/ds1620.c
+++ b/drivers/char/ds1620.c
@@ -43,52 +43,51 @@ static const char *fan_state[] = { "off", "on", "on (hardwired)" };
* chance that the WaveArtist driver could touch these bits to
* enable or disable the speaker.
*/
-extern spinlock_t gpio_lock;
extern unsigned int system_rev;
static inline void netwinder_ds1620_set_clk(int clk)
{
- gpio_modify_op(GPIO_DSCLK, clk ? GPIO_DSCLK : 0);
+ nw_gpio_modify_op(GPIO_DSCLK, clk ? GPIO_DSCLK : 0);
}
static inline void netwinder_ds1620_set_data(int dat)
{
- gpio_modify_op(GPIO_DATA, dat ? GPIO_DATA : 0);
+ nw_gpio_modify_op(GPIO_DATA, dat ? GPIO_DATA : 0);
}
static inline int netwinder_ds1620_get_data(void)
{
- return gpio_read() & GPIO_DATA;
+ return nw_gpio_read() & GPIO_DATA;
}
static inline void netwinder_ds1620_set_data_dir(int dir)
{
- gpio_modify_io(GPIO_DATA, dir ? GPIO_DATA : 0);
+ nw_gpio_modify_io(GPIO_DATA, dir ? GPIO_DATA : 0);
}
static inline void netwinder_ds1620_reset(void)
{
- cpld_modify(CPLD_DS_ENABLE, 0);
- cpld_modify(CPLD_DS_ENABLE, CPLD_DS_ENABLE);
+ nw_cpld_modify(CPLD_DS_ENABLE, 0);
+ nw_cpld_modify(CPLD_DS_ENABLE, CPLD_DS_ENABLE);
}
static inline void netwinder_lock(unsigned long *flags)
{
- spin_lock_irqsave(&gpio_lock, *flags);
+ spin_lock_irqsave(&nw_gpio_lock, *flags);
}
static inline void netwinder_unlock(unsigned long *flags)
{
- spin_unlock_irqrestore(&gpio_lock, *flags);
+ spin_unlock_irqrestore(&nw_gpio_lock, *flags);
}
static inline void netwinder_set_fan(int i)
{
unsigned long flags;
- spin_lock_irqsave(&gpio_lock, flags);
- gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ nw_gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0);
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
}
static inline int netwinder_get_fan(void)
@@ -96,7 +95,7 @@ static inline int netwinder_get_fan(void)
if ((system_rev & 0xf000) == 0x4000)
return FAN_ALWAYS_ON;
- return (gpio_read() & GPIO_FAN) ? FAN_ON : FAN_OFF;
+ return (nw_gpio_read() & GPIO_FAN) ? FAN_ON : FAN_OFF;
}
/*
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 53fdc7ff3870..32b8bbf5003e 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -46,7 +46,7 @@
/*
* The High Precision Event Timer driver.
* This driver is closely modelled after the rtc.c driver.
- * http://www.intel.com/hardwaredesign/hpetspec.htm
+ * http://www.intel.com/hardwaredesign/hpetspec_1.pdf
*/
#define HPET_USER_FREQ (64)
#define HPET_DRIFT (500)
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index fb57f67bb427..0587b66d6fc7 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -695,6 +695,7 @@ void hvc_resize(struct hvc_struct *hp, struct winsize ws)
hp->ws = ws;
schedule_work(&hp->tty_resize);
}
+EXPORT_SYMBOL_GPL(hvc_resize);
/*
* This kthread is either polling or interrupt driven. This is determined by
diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c
index 006be92ee3f3..8c7df5ba088f 100644
--- a/drivers/char/nwflash.c
+++ b/drivers/char/nwflash.c
@@ -58,8 +58,6 @@ static volatile unsigned char *FLASH_BASE;
static int gbFlashSize = KFLASH_SIZE;
static DEFINE_MUTEX(nwflash_mutex);
-extern spinlock_t gpio_lock;
-
static int get_flash_id(void)
{
volatile unsigned int c1, c2;
@@ -616,9 +614,9 @@ static void kick_open(void)
* we want to write a bit pattern XXX1 to Xilinx to enable
* the write gate, which will be open for about the next 2ms.
*/
- spin_lock_irqsave(&gpio_lock, flags);
- cpld_modify(1, 1);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
/*
* let the ISA bus to catch on...
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 675076f5fca8..d26891bfcd41 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -558,23 +558,9 @@ struct timer_rand_state {
unsigned dont_count_entropy:1;
};
-static struct timer_rand_state *irq_timer_state[NR_IRQS];
-
-static struct timer_rand_state *get_timer_rand_state(unsigned int irq)
-{
- if (irq >= nr_irqs)
- return NULL;
-
- return irq_timer_state[irq];
-}
-
-static void set_timer_rand_state(unsigned int irq, struct timer_rand_state *state)
-{
- if (irq >= nr_irqs)
- return;
-
- irq_timer_state[irq] = state;
-}
+#ifndef CONFIG_SPARSE_IRQ
+struct timer_rand_state *irq_timer_state[NR_IRQS];
+#endif
static struct timer_rand_state input_timer_state;
@@ -933,8 +919,10 @@ void rand_initialize_irq(int irq)
{
struct timer_rand_state *state;
+#ifndef CONFIG_SPARSE_IRQ
if (irq >= nr_irqs)
return;
+#endif
state = get_timer_rand_state(irq);
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 3fb0d2c88ba5..ff6f5a4b58fb 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -138,12 +138,33 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
}
/*
+ * virtio console configuration. This supports:
+ * - console resize
+ */
+static void virtcons_apply_config(struct virtio_device *dev)
+{
+ struct winsize ws;
+
+ if (virtio_has_feature(dev, VIRTIO_CONSOLE_F_SIZE)) {
+ dev->config->get(dev,
+ offsetof(struct virtio_console_config, cols),
+ &ws.ws_col, sizeof(u16));
+ dev->config->get(dev,
+ offsetof(struct virtio_console_config, rows),
+ &ws.ws_row, sizeof(u16));
+ hvc_resize(hvc, ws);
+ }
+}
+
+/*
* we support only one console, the hvc struct is a global var
- * There is no need to do anything
+ * We set the configuration at this point, since we now have a tty
*/
static int notifier_add_vio(struct hvc_struct *hp, int data)
{
hp->irq_requested = 1;
+ virtcons_apply_config(vdev);
+
return 0;
}
@@ -234,11 +255,18 @@ static struct virtio_device_id id_table[] = {
{ 0 },
};
+static unsigned int features[] = {
+ VIRTIO_CONSOLE_F_SIZE,
+};
+
static struct virtio_driver virtio_console = {
+ .feature_table = features,
+ .feature_table_size = ARRAY_SIZE(features),
.driver.name = KBUILD_MODNAME,
.driver.owner = THIS_MODULE,
.id_table = id_table,
.probe = virtcons_probe,
+ .config_changed = virtcons_apply_config,
};
static int __init init(void)
diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c
index c20171078d1d..e1129fad96dd 100644
--- a/drivers/clocksource/acpi_pm.c
+++ b/drivers/clocksource/acpi_pm.c
@@ -57,11 +57,6 @@ u32 acpi_pm_read_verified(void)
return v2;
}
-static cycle_t acpi_pm_read_slow(void)
-{
- return (cycle_t)acpi_pm_read_verified();
-}
-
static cycle_t acpi_pm_read(void)
{
return (cycle_t)read_pmtmr();
@@ -88,6 +83,11 @@ static int __init acpi_pm_good_setup(char *__str)
}
__setup("acpi_pm_good", acpi_pm_good_setup);
+static cycle_t acpi_pm_read_slow(void)
+{
+ return (cycle_t)acpi_pm_read_verified();
+}
+
static inline void acpi_pm_need_workaround(void)
{
clocksource_acpi_pm.read = acpi_pm_read_slow;
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 4a597d8c2f70..78b989d202a3 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -582,3 +582,19 @@ int dmi_walk(void (*decode)(const struct dmi_header *))
return 0;
}
EXPORT_SYMBOL_GPL(dmi_walk);
+
+/**
+ * dmi_match - compare a string to the dmi field (if exists)
+ *
+ * Returns true if the requested field equals to the str (including NULL).
+ */
+bool dmi_match(enum dmi_field f, const char *str)
+{
+ const char *info = dmi_get_system_info(f);
+
+ if (info == NULL || str == NULL)
+ return info == str;
+
+ return !strcmp(info, str);
+}
+EXPORT_SYMBOL_GPL(dmi_match);
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index a8b33c2ec8d2..5130b72d593c 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -7,6 +7,8 @@
menuconfig DRM
tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)"
depends on (AGP || AGP=n) && PCI && !EMULATED_CMPXCHG && MMU
+ select I2C
+ select I2C_ALGOBIT
help
Kernel-level support for the Direct Rendering Infrastructure (DRI)
introduced in XFree86 4.0. If you say Y here, you need to select
@@ -65,6 +67,10 @@ config DRM_I830
will load the correct one.
config DRM_I915
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ depends on FB
tristate "i915 driver"
help
Choose this option if you have a system that has Intel 830M, 845G,
@@ -76,6 +82,17 @@ config DRM_I915
endchoice
+config DRM_I915_KMS
+ bool "Enable modesetting on intel by default"
+ depends on DRM_I915
+ help
+ Choose this option if you want kernel modesetting enabled by default,
+ and you have a new enough userspace to support this. Running old
+ userspaces with this enabled will cause pain. Note that this causes
+ the driver to bind to PCI devices, which precludes loading things
+ like intelfb.
+
+
config DRM_MGA
tristate "Matrox g200/g400"
depends on DRM
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 74da99495e21..30022c4a5c12 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -9,7 +9,8 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \
drm_drv.o drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \
drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \
drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \
- drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o
+ drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o \
+ drm_crtc.o drm_crtc_helper.o drm_modes.o drm_edid.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
index a73462723d2d..ca7a9ef5007b 100644
--- a/drivers/gpu/drm/drm_auth.c
+++ b/drivers/gpu/drm/drm_auth.c
@@ -45,14 +45,15 @@
* the one with matching magic number, while holding the drm_device::struct_mutex
* lock.
*/
-static struct drm_file *drm_find_file(struct drm_device * dev, drm_magic_t magic)
+static struct drm_file *drm_find_file(struct drm_master *master, drm_magic_t magic)
{
struct drm_file *retval = NULL;
struct drm_magic_entry *pt;
struct drm_hash_item *hash;
+ struct drm_device *dev = master->minor->dev;
mutex_lock(&dev->struct_mutex);
- if (!drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) {
+ if (!drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {
pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);
retval = pt->priv;
}
@@ -71,11 +72,11 @@ static struct drm_file *drm_find_file(struct drm_device * dev, drm_magic_t magic
* associated the magic number hash key in drm_device::magiclist, while holding
* the drm_device::struct_mutex lock.
*/
-static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,
+static int drm_add_magic(struct drm_master *master, struct drm_file *priv,
drm_magic_t magic)
{
struct drm_magic_entry *entry;
-
+ struct drm_device *dev = master->minor->dev;
DRM_DEBUG("%d\n", magic);
entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC);
@@ -83,11 +84,10 @@ static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,
return -ENOMEM;
memset(entry, 0, sizeof(*entry));
entry->priv = priv;
-
entry->hash_item.key = (unsigned long)magic;
mutex_lock(&dev->struct_mutex);
- drm_ht_insert_item(&dev->magiclist, &entry->hash_item);
- list_add_tail(&entry->head, &dev->magicfree);
+ drm_ht_insert_item(&master->magiclist, &entry->hash_item);
+ list_add_tail(&entry->head, &master->magicfree);
mutex_unlock(&dev->struct_mutex);
return 0;
@@ -102,20 +102,21 @@ static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,
* Searches and unlinks the entry in drm_device::magiclist with the magic
* number hash key, while holding the drm_device::struct_mutex lock.
*/
-static int drm_remove_magic(struct drm_device * dev, drm_magic_t magic)
+static int drm_remove_magic(struct drm_master *master, drm_magic_t magic)
{
struct drm_magic_entry *pt;
struct drm_hash_item *hash;
+ struct drm_device *dev = master->minor->dev;
DRM_DEBUG("%d\n", magic);
mutex_lock(&dev->struct_mutex);
- if (drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) {
+ if (drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
}
pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);
- drm_ht_remove_item(&dev->magiclist, hash);
+ drm_ht_remove_item(&master->magiclist, hash);
list_del(&pt->head);
mutex_unlock(&dev->struct_mutex);
@@ -153,9 +154,9 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
++sequence; /* reserve 0 */
auth->magic = sequence++;
spin_unlock(&lock);
- } while (drm_find_file(dev, auth->magic));
+ } while (drm_find_file(file_priv->master, auth->magic));
file_priv->magic = auth->magic;
- drm_add_magic(dev, file_priv, auth->magic);
+ drm_add_magic(file_priv->master, file_priv, auth->magic);
}
DRM_DEBUG("%u\n", auth->magic);
@@ -181,9 +182,9 @@ int drm_authmagic(struct drm_device *dev, void *data,
struct drm_file *file;
DRM_DEBUG("%u\n", auth->magic);
- if ((file = drm_find_file(dev, auth->magic))) {
+ if ((file = drm_find_file(file_priv->master, auth->magic))) {
file->authenticated = 1;
- drm_remove_magic(dev, auth->magic);
+ drm_remove_magic(file_priv->master, auth->magic);
return 0;
}
return -EINVAL;
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index bde64b84166e..72c667f9bee1 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -54,9 +54,9 @@ static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
{
struct drm_map_list *entry;
list_for_each_entry(entry, &dev->maplist, head) {
- if (entry->map && map->type == entry->map->type &&
+ if (entry->map && (entry->master == dev->primary->master) && (map->type == entry->map->type) &&
((entry->map->offset == map->offset) ||
- (map->type == _DRM_SHM && map->flags==_DRM_CONTAINS_LOCK))) {
+ ((map->type == _DRM_SHM) && (map->flags&_DRM_CONTAINS_LOCK)))) {
return entry;
}
}
@@ -210,12 +210,12 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
map->offset = (unsigned long)map->handle;
if (map->flags & _DRM_CONTAINS_LOCK) {
/* Prevent a 2nd X Server from creating a 2nd lock */
- if (dev->lock.hw_lock != NULL) {
+ if (dev->primary->master->lock.hw_lock != NULL) {
vfree(map->handle);
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
return -EBUSY;
}
- dev->sigdata.lock = dev->lock.hw_lock = map->handle; /* Pointer to lock */
+ dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle; /* Pointer to lock */
}
break;
case _DRM_AGP: {
@@ -262,6 +262,9 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
DRM_DEBUG("AGP offset = 0x%08lx, size = 0x%08lx\n", map->offset, map->size);
break;
+ case _DRM_GEM:
+ DRM_ERROR("tried to rmmap GEM object\n");
+ break;
}
case _DRM_SCATTER_GATHER:
if (!dev->sg) {
@@ -319,6 +322,7 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
list->user_token = list->hash.key << PAGE_SHIFT;
mutex_unlock(&dev->struct_mutex);
+ list->master = dev->primary->master;
*maplist = list;
return 0;
}
@@ -345,7 +349,7 @@ int drm_addmap_ioctl(struct drm_device *dev, void *data,
struct drm_map_list *maplist;
int err;
- if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP))
+ if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP || map->type == _DRM_SHM))
return -EPERM;
err = drm_addmap_core(dev, map->offset, map->size, map->type,
@@ -380,10 +384,12 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
struct drm_map_list *r_list = NULL, *list_t;
drm_dma_handle_t dmah;
int found = 0;
+ struct drm_master *master;
/* Find the list entry for the map and remove it */
list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
if (r_list->map == map) {
+ master = r_list->master;
list_del(&r_list->head);
drm_ht_remove_key(&dev->map_hash,
r_list->user_token >> PAGE_SHIFT);
@@ -409,6 +415,13 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
break;
case _DRM_SHM:
vfree(map->handle);
+ if (master) {
+ if (dev->sigdata.lock == master->lock.hw_lock)
+ dev->sigdata.lock = NULL;
+ master->lock.hw_lock = NULL; /* SHM removed */
+ master->lock.file_priv = NULL;
+ wake_up_interruptible(&master->lock.lock_queue);
+ }
break;
case _DRM_AGP:
case _DRM_SCATTER_GATHER:
@@ -419,11 +432,15 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
dmah.size = map->size;
__drm_pci_free(dev, &dmah);
break;
+ case _DRM_GEM:
+ DRM_ERROR("tried to rmmap GEM object\n");
+ break;
}
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
return 0;
}
+EXPORT_SYMBOL(drm_rmmap_locked);
int drm_rmmap(struct drm_device *dev, drm_local_map_t *map)
{
diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c
index d505f695421f..809ec0f03452 100644
--- a/drivers/gpu/drm/drm_context.c
+++ b/drivers/gpu/drm/drm_context.c
@@ -256,12 +256,13 @@ static int drm_context_switch(struct drm_device * dev, int old, int new)
* hardware lock is held, clears the drm_device::context_flag and wakes up
* drm_device::context_wait.
*/
-static int drm_context_switch_complete(struct drm_device * dev, int new)
+static int drm_context_switch_complete(struct drm_device *dev,
+ struct drm_file *file_priv, int new)
{
dev->last_context = new; /* PRE/POST: This is the _only_ writer. */
dev->last_switch = jiffies;
- if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ if (!_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock)) {
DRM_ERROR("Lock isn't held after context switch\n");
}
@@ -420,7 +421,7 @@ int drm_newctx(struct drm_device *dev, void *data,
struct drm_ctx *ctx = data;
DRM_DEBUG("%d\n", ctx->handle);
- drm_context_switch_complete(dev, ctx->handle);
+ drm_context_switch_complete(dev, file_priv, ctx->handle);
return 0;
}
@@ -442,9 +443,6 @@ int drm_rmctx(struct drm_device *dev, void *data,
struct drm_ctx *ctx = data;
DRM_DEBUG("%d\n", ctx->handle);
- if (ctx->handle == DRM_KERNEL_CONTEXT + 1) {
- file_priv->remove_auth_on_close = 1;
- }
if (ctx->handle != DRM_KERNEL_CONTEXT) {
if (dev->driver->context_dtor)
dev->driver->context_dtor(dev, ctx->handle);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
new file mode 100644
index 000000000000..53c87254be4c
--- /dev/null
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -0,0 +1,2446 @@
+/*
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (c) 2008 Red Hat Inc.
+ *
+ * DRM core CRTC related functions
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Keith Packard
+ * Eric Anholt <eric@anholt.net>
+ * Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+#include <linux/list.h>
+#include "drm.h"
+#include "drmP.h"
+#include "drm_crtc.h"
+
+struct drm_prop_enum_list {
+ int type;
+ char *name;
+};
+
+/* Avoid boilerplate. I'm tired of typing. */
+#define DRM_ENUM_NAME_FN(fnname, list) \
+ char *fnname(int val) \
+ { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(list); i++) { \
+ if (list[i].type == val) \
+ return list[i].name; \
+ } \
+ return "(unknown)"; \
+ }
+
+/*
+ * Global properties
+ */
+static struct drm_prop_enum_list drm_dpms_enum_list[] =
+{ { DRM_MODE_DPMS_ON, "On" },
+ { DRM_MODE_DPMS_STANDBY, "Standby" },
+ { DRM_MODE_DPMS_SUSPEND, "Suspend" },
+ { DRM_MODE_DPMS_OFF, "Off" }
+};
+
+DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
+
+/*
+ * Optional properties
+ */
+static struct drm_prop_enum_list drm_scaling_mode_enum_list[] =
+{
+ { DRM_MODE_SCALE_NON_GPU, "Non-GPU" },
+ { DRM_MODE_SCALE_FULLSCREEN, "Fullscreen" },
+ { DRM_MODE_SCALE_NO_SCALE, "No scale" },
+ { DRM_MODE_SCALE_ASPECT, "Aspect" },
+};
+
+static struct drm_prop_enum_list drm_dithering_mode_enum_list[] =
+{
+ { DRM_MODE_DITHERING_OFF, "Off" },
+ { DRM_MODE_DITHERING_ON, "On" },
+};
+
+/*
+ * Non-global properties, but "required" for certain connectors.
+ */
+static struct drm_prop_enum_list drm_dvi_i_select_enum_list[] =
+{
+ { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
+ { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */
+ { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */
+};
+
+DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
+
+static struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] =
+{
+ { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */
+ { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */
+ { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */
+};
+
+DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
+ drm_dvi_i_subconnector_enum_list)
+
+static struct drm_prop_enum_list drm_tv_select_enum_list[] =
+{
+ { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
+ { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
+ { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */
+ { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
+};
+
+DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
+
+static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] =
+{
+ { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */
+ { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
+ { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */
+ { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
+};
+
+DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
+ drm_tv_subconnector_enum_list)
+
+struct drm_conn_prop_enum_list {
+ int type;
+ char *name;
+ int count;
+};
+
+/*
+ * Connector and encoder types.
+ */
+static struct drm_conn_prop_enum_list drm_connector_enum_list[] =
+{ { DRM_MODE_CONNECTOR_Unknown, "Unknown", 0 },
+ { DRM_MODE_CONNECTOR_VGA, "VGA", 0 },
+ { DRM_MODE_CONNECTOR_DVII, "DVI-I", 0 },
+ { DRM_MODE_CONNECTOR_DVID, "DVI-D", 0 },
+ { DRM_MODE_CONNECTOR_DVIA, "DVI-A", 0 },
+ { DRM_MODE_CONNECTOR_Composite, "Composite", 0 },
+ { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO", 0 },
+ { DRM_MODE_CONNECTOR_LVDS, "LVDS", 0 },
+ { DRM_MODE_CONNECTOR_Component, "Component", 0 },
+ { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN", 0 },
+ { DRM_MODE_CONNECTOR_DisplayPort, "DisplayPort", 0 },
+ { DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A", 0 },
+ { DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B", 0 },
+};
+
+static struct drm_prop_enum_list drm_encoder_enum_list[] =
+{ { DRM_MODE_ENCODER_NONE, "None" },
+ { DRM_MODE_ENCODER_DAC, "DAC" },
+ { DRM_MODE_ENCODER_TMDS, "TMDS" },
+ { DRM_MODE_ENCODER_LVDS, "LVDS" },
+ { DRM_MODE_ENCODER_TVDAC, "TV" },
+};
+
+char *drm_get_encoder_name(struct drm_encoder *encoder)
+{
+ static char buf[32];
+
+ snprintf(buf, 32, "%s-%d",
+ drm_encoder_enum_list[encoder->encoder_type].name,
+ encoder->base.id);
+ return buf;
+}
+
+char *drm_get_connector_name(struct drm_connector *connector)
+{
+ static char buf[32];
+
+ snprintf(buf, 32, "%s-%d",
+ drm_connector_enum_list[connector->connector_type].name,
+ connector->connector_type_id);
+ return buf;
+}
+EXPORT_SYMBOL(drm_get_connector_name);
+
+char *drm_get_connector_status_name(enum drm_connector_status status)
+{
+ if (status == connector_status_connected)
+ return "connected";
+ else if (status == connector_status_disconnected)
+ return "disconnected";
+ else
+ return "unknown";
+}
+
+/**
+ * drm_mode_object_get - allocate a new identifier
+ * @dev: DRM device
+ * @ptr: object pointer, used to generate unique ID
+ * @type: object type
+ *
+ * LOCKING:
+ * Caller must hold DRM mode_config lock.
+ *
+ * Create a unique identifier based on @ptr in @dev's identifier space. Used
+ * for tracking modes, CRTCs and connectors.
+ *
+ * RETURNS:
+ * New unique (relative to other objects in @dev) integer identifier for the
+ * object.
+ */
+static int drm_mode_object_get(struct drm_device *dev,
+ struct drm_mode_object *obj, uint32_t obj_type)
+{
+ int new_id = 0;
+ int ret;
+
+ WARN(!mutex_is_locked(&dev->mode_config.mutex),
+ "%s called w/o mode_config lock\n", __FUNCTION__);
+again:
+ if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) {
+ DRM_ERROR("Ran out memory getting a mode number\n");
+ return -EINVAL;
+ }
+
+ ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id);
+ if (ret == -EAGAIN)
+ goto again;
+
+ obj->id = new_id;
+ obj->type = obj_type;
+ return 0;
+}
+
+/**
+ * drm_mode_object_put - free an identifer
+ * @dev: DRM device
+ * @id: ID to free
+ *
+ * LOCKING:
+ * Caller must hold DRM mode_config lock.
+ *
+ * Free @id from @dev's unique identifier pool.
+ */
+static void drm_mode_object_put(struct drm_device *dev,
+ struct drm_mode_object *object)
+{
+ idr_remove(&dev->mode_config.crtc_idr, object->id);
+}
+
+void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type)
+{
+ struct drm_mode_object *obj;
+
+ obj = idr_find(&dev->mode_config.crtc_idr, id);
+ if (!obj || (obj->type != type) || (obj->id != id))
+ return NULL;
+
+ return obj;
+}
+EXPORT_SYMBOL(drm_mode_object_find);
+
+/**
+ * drm_crtc_from_fb - find the CRTC structure associated with an fb
+ * @dev: DRM device
+ * @fb: framebuffer in question
+ *
+ * LOCKING:
+ * Caller must hold mode_config lock.
+ *
+ * Find CRTC in the mode_config structure that matches @fb.
+ *
+ * RETURNS:
+ * Pointer to the CRTC or NULL if it wasn't found.
+ */
+struct drm_crtc *drm_crtc_from_fb(struct drm_device *dev,
+ struct drm_framebuffer *fb)
+{
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (crtc->fb == fb)
+ return crtc;
+ }
+ return NULL;
+}
+
+/**
+ * drm_framebuffer_init - initialize a framebuffer
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Allocates an ID for the framebuffer's parent mode object, sets its mode
+ * functions & device file and adds it to the master fd list.
+ *
+ * RETURNS:
+ * Zero on success, error code on falure.
+ */
+int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
+ const struct drm_framebuffer_funcs *funcs)
+{
+ int ret;
+
+ ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
+ if (ret) {
+ return ret;
+ }
+
+ fb->dev = dev;
+ fb->funcs = funcs;
+ dev->mode_config.num_fb++;
+ list_add(&fb->head, &dev->mode_config.fb_list);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_framebuffer_init);
+
+/**
+ * drm_framebuffer_cleanup - remove a framebuffer object
+ * @fb: framebuffer to remove
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes
+ * it, setting it to NULL.
+ */
+void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
+{
+ struct drm_device *dev = fb->dev;
+ struct drm_crtc *crtc;
+
+ /* remove from any CRTC */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (crtc->fb == fb)
+ crtc->fb = NULL;
+ }
+
+ drm_mode_object_put(dev, &fb->base);
+ list_del(&fb->head);
+ dev->mode_config.num_fb--;
+}
+EXPORT_SYMBOL(drm_framebuffer_cleanup);
+
+/**
+ * drm_crtc_init - Initialise a new CRTC object
+ * @dev: DRM device
+ * @crtc: CRTC object to init
+ * @funcs: callbacks for the new CRTC
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Inits a new object created as base part of an driver crtc object.
+ */
+void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+ const struct drm_crtc_funcs *funcs)
+{
+ crtc->dev = dev;
+ crtc->funcs = funcs;
+
+ mutex_lock(&dev->mode_config.mutex);
+ drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
+
+ list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
+ dev->mode_config.num_crtc++;
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_crtc_init);
+
+/**
+ * drm_crtc_cleanup - Cleans up the core crtc usage.
+ * @crtc: CRTC to cleanup
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Cleanup @crtc. Removes from drm modesetting space
+ * does NOT free object, caller does that.
+ */
+void drm_crtc_cleanup(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+
+ if (crtc->gamma_store) {
+ kfree(crtc->gamma_store);
+ crtc->gamma_store = NULL;
+ }
+
+ drm_mode_object_put(dev, &crtc->base);
+ list_del(&crtc->head);
+ dev->mode_config.num_crtc--;
+}
+EXPORT_SYMBOL(drm_crtc_cleanup);
+
+/**
+ * drm_mode_probed_add - add a mode to a connector's probed mode list
+ * @connector: connector the new mode
+ * @mode: mode data
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Add @mode to @connector's mode list for later use.
+ */
+void drm_mode_probed_add(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ list_add(&mode->head, &connector->probed_modes);
+}
+EXPORT_SYMBOL(drm_mode_probed_add);
+
+/**
+ * drm_mode_remove - remove and free a mode
+ * @connector: connector list to modify
+ * @mode: mode to remove
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Remove @mode from @connector's mode list, then free it.
+ */
+void drm_mode_remove(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ list_del(&mode->head);
+ kfree(mode);
+}
+EXPORT_SYMBOL(drm_mode_remove);
+
+/**
+ * drm_connector_init - Init a preallocated connector
+ * @dev: DRM device
+ * @connector: the connector to init
+ * @funcs: callbacks for this connector
+ * @name: user visible name of the connector
+ *
+ * LOCKING:
+ * Caller must hold @dev's mode_config lock.
+ *
+ * Initialises a preallocated connector. Connectors should be
+ * subclassed as part of driver connector objects.
+ */
+void drm_connector_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ int connector_type)
+{
+ mutex_lock(&dev->mode_config.mutex);
+
+ connector->dev = dev;
+ connector->funcs = funcs;
+ drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
+ connector->connector_type = connector_type;
+ connector->connector_type_id =
+ ++drm_connector_enum_list[connector_type].count; /* TODO */
+ INIT_LIST_HEAD(&connector->user_modes);
+ INIT_LIST_HEAD(&connector->probed_modes);
+ INIT_LIST_HEAD(&connector->modes);
+ connector->edid_blob_ptr = NULL;
+
+ list_add_tail(&connector->head, &dev->mode_config.connector_list);
+ dev->mode_config.num_connector++;
+
+ drm_connector_attach_property(connector,
+ dev->mode_config.edid_property, 0);
+
+ drm_connector_attach_property(connector,
+ dev->mode_config.dpms_property, 0);
+
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_connector_init);
+
+/**
+ * drm_connector_cleanup - cleans up an initialised connector
+ * @connector: connector to cleanup
+ *
+ * LOCKING:
+ * Caller must hold @dev's mode_config lock.
+ *
+ * Cleans up the connector but doesn't free the object.
+ */
+void drm_connector_cleanup(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_display_mode *mode, *t;
+
+ list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
+ drm_mode_remove(connector, mode);
+
+ list_for_each_entry_safe(mode, t, &connector->modes, head)
+ drm_mode_remove(connector, mode);
+
+ list_for_each_entry_safe(mode, t, &connector->user_modes, head)
+ drm_mode_remove(connector, mode);
+
+ mutex_lock(&dev->mode_config.mutex);
+ drm_mode_object_put(dev, &connector->base);
+ list_del(&connector->head);
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_connector_cleanup);
+
+void drm_encoder_init(struct drm_device *dev,
+ struct drm_encoder *encoder,
+ const struct drm_encoder_funcs *funcs,
+ int encoder_type)
+{
+ mutex_lock(&dev->mode_config.mutex);
+
+ encoder->dev = dev;
+
+ drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
+ encoder->encoder_type = encoder_type;
+ encoder->funcs = funcs;
+
+ list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
+ dev->mode_config.num_encoder++;
+
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_encoder_init);
+
+void drm_encoder_cleanup(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ mutex_lock(&dev->mode_config.mutex);
+ drm_mode_object_put(dev, &encoder->base);
+ list_del(&encoder->head);
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_encoder_cleanup);
+
+/**
+ * drm_mode_create - create a new display mode
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller must hold DRM mode_config lock.
+ *
+ * Create a new drm_display_mode, give it an ID, and return it.
+ *
+ * RETURNS:
+ * Pointer to new mode on success, NULL on error.
+ */
+struct drm_display_mode *drm_mode_create(struct drm_device *dev)
+{
+ struct drm_display_mode *nmode;
+
+ nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
+ if (!nmode)
+ return NULL;
+
+ drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE);
+ return nmode;
+}
+EXPORT_SYMBOL(drm_mode_create);
+
+/**
+ * drm_mode_destroy - remove a mode
+ * @dev: DRM device
+ * @mode: mode to remove
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Free @mode's unique identifier, then free it.
+ */
+void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
+{
+ drm_mode_object_put(dev, &mode->base);
+
+ kfree(mode);
+}
+EXPORT_SYMBOL(drm_mode_destroy);
+
+static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
+{
+ struct drm_property *edid;
+ struct drm_property *dpms;
+ int i;
+
+ /*
+ * Standard properties (apply to all connectors)
+ */
+ edid = drm_property_create(dev, DRM_MODE_PROP_BLOB |
+ DRM_MODE_PROP_IMMUTABLE,
+ "EDID", 0);
+ dev->mode_config.edid_property = edid;
+
+ dpms = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ "DPMS", ARRAY_SIZE(drm_dpms_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++)
+ drm_property_add_enum(dpms, i, drm_dpms_enum_list[i].type,
+ drm_dpms_enum_list[i].name);
+ dev->mode_config.dpms_property = dpms;
+
+ return 0;
+}
+
+/**
+ * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
+ * @dev: DRM device
+ *
+ * Called by a driver the first time a DVI-I connector is made.
+ */
+int drm_mode_create_dvi_i_properties(struct drm_device *dev)
+{
+ struct drm_property *dvi_i_selector;
+ struct drm_property *dvi_i_subconnector;
+ int i;
+
+ if (dev->mode_config.dvi_i_select_subconnector_property)
+ return 0;
+
+ dvi_i_selector =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ "select subconnector",
+ ARRAY_SIZE(drm_dvi_i_select_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_dvi_i_select_enum_list); i++)
+ drm_property_add_enum(dvi_i_selector, i,
+ drm_dvi_i_select_enum_list[i].type,
+ drm_dvi_i_select_enum_list[i].name);
+ dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
+
+ dvi_i_subconnector =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM |
+ DRM_MODE_PROP_IMMUTABLE,
+ "subconnector",
+ ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_dvi_i_subconnector_enum_list); i++)
+ drm_property_add_enum(dvi_i_subconnector, i,
+ drm_dvi_i_subconnector_enum_list[i].type,
+ drm_dvi_i_subconnector_enum_list[i].name);
+ dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
+
+/**
+ * drm_create_tv_properties - create TV specific connector properties
+ * @dev: DRM device
+ * @num_modes: number of different TV formats (modes) supported
+ * @modes: array of pointers to strings containing name of each format
+ *
+ * Called by a driver's TV initialization routine, this function creates
+ * the TV specific connector properties for a given device. Caller is
+ * responsible for allocating a list of format names and passing them to
+ * this routine.
+ */
+int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes,
+ char *modes[])
+{
+ struct drm_property *tv_selector;
+ struct drm_property *tv_subconnector;
+ int i;
+
+ if (dev->mode_config.tv_select_subconnector_property)
+ return 0;
+
+ /*
+ * Basic connector properties
+ */
+ tv_selector = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ "select subconnector",
+ ARRAY_SIZE(drm_tv_select_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_tv_select_enum_list); i++)
+ drm_property_add_enum(tv_selector, i,
+ drm_tv_select_enum_list[i].type,
+ drm_tv_select_enum_list[i].name);
+ dev->mode_config.tv_select_subconnector_property = tv_selector;
+
+ tv_subconnector =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM |
+ DRM_MODE_PROP_IMMUTABLE, "subconnector",
+ ARRAY_SIZE(drm_tv_subconnector_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_tv_subconnector_enum_list); i++)
+ drm_property_add_enum(tv_subconnector, i,
+ drm_tv_subconnector_enum_list[i].type,
+ drm_tv_subconnector_enum_list[i].name);
+ dev->mode_config.tv_subconnector_property = tv_subconnector;
+
+ /*
+ * Other, TV specific properties: margins & TV modes.
+ */
+ dev->mode_config.tv_left_margin_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "left margin", 2);
+ dev->mode_config.tv_left_margin_property->values[0] = 0;
+ dev->mode_config.tv_left_margin_property->values[1] = 100;
+
+ dev->mode_config.tv_right_margin_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "right margin", 2);
+ dev->mode_config.tv_right_margin_property->values[0] = 0;
+ dev->mode_config.tv_right_margin_property->values[1] = 100;
+
+ dev->mode_config.tv_top_margin_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "top margin", 2);
+ dev->mode_config.tv_top_margin_property->values[0] = 0;
+ dev->mode_config.tv_top_margin_property->values[1] = 100;
+
+ dev->mode_config.tv_bottom_margin_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "bottom margin", 2);
+ dev->mode_config.tv_bottom_margin_property->values[0] = 0;
+ dev->mode_config.tv_bottom_margin_property->values[1] = 100;
+
+ dev->mode_config.tv_mode_property =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ "mode", num_modes);
+ for (i = 0; i < num_modes; i++)
+ drm_property_add_enum(dev->mode_config.tv_mode_property, i,
+ i, modes[i]);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_tv_properties);
+
+/**
+ * drm_mode_create_scaling_mode_property - create scaling mode property
+ * @dev: DRM device
+ *
+ * Called by a driver the first time it's needed, must be attached to desired
+ * connectors.
+ */
+int drm_mode_create_scaling_mode_property(struct drm_device *dev)
+{
+ struct drm_property *scaling_mode;
+ int i;
+
+ if (dev->mode_config.scaling_mode_property)
+ return 0;
+
+ scaling_mode =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode",
+ ARRAY_SIZE(drm_scaling_mode_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++)
+ drm_property_add_enum(scaling_mode, i,
+ drm_scaling_mode_enum_list[i].type,
+ drm_scaling_mode_enum_list[i].name);
+
+ dev->mode_config.scaling_mode_property = scaling_mode;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
+
+/**
+ * drm_mode_create_dithering_property - create dithering property
+ * @dev: DRM device
+ *
+ * Called by a driver the first time it's needed, must be attached to desired
+ * connectors.
+ */
+int drm_mode_create_dithering_property(struct drm_device *dev)
+{
+ struct drm_property *dithering_mode;
+ int i;
+
+ if (dev->mode_config.dithering_mode_property)
+ return 0;
+
+ dithering_mode =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM, "dithering",
+ ARRAY_SIZE(drm_dithering_mode_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_dithering_mode_enum_list); i++)
+ drm_property_add_enum(dithering_mode, i,
+ drm_dithering_mode_enum_list[i].type,
+ drm_dithering_mode_enum_list[i].name);
+ dev->mode_config.dithering_mode_property = dithering_mode;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_dithering_property);
+
+/**
+ * drm_mode_config_init - initialize DRM mode_configuration structure
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * None, should happen single threaded at init time.
+ *
+ * Initialize @dev's mode_config structure, used for tracking the graphics
+ * configuration of @dev.
+ */
+void drm_mode_config_init(struct drm_device *dev)
+{
+ mutex_init(&dev->mode_config.mutex);
+ INIT_LIST_HEAD(&dev->mode_config.fb_list);
+ INIT_LIST_HEAD(&dev->mode_config.fb_kernel_list);
+ INIT_LIST_HEAD(&dev->mode_config.crtc_list);
+ INIT_LIST_HEAD(&dev->mode_config.connector_list);
+ INIT_LIST_HEAD(&dev->mode_config.encoder_list);
+ INIT_LIST_HEAD(&dev->mode_config.property_list);
+ INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
+ idr_init(&dev->mode_config.crtc_idr);
+
+ mutex_lock(&dev->mode_config.mutex);
+ drm_mode_create_standard_connector_properties(dev);
+ mutex_unlock(&dev->mode_config.mutex);
+
+ /* Just to be sure */
+ dev->mode_config.num_fb = 0;
+ dev->mode_config.num_connector = 0;
+ dev->mode_config.num_crtc = 0;
+ dev->mode_config.num_encoder = 0;
+}
+EXPORT_SYMBOL(drm_mode_config_init);
+
+int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group)
+{
+ uint32_t total_objects = 0;
+
+ total_objects += dev->mode_config.num_crtc;
+ total_objects += dev->mode_config.num_connector;
+ total_objects += dev->mode_config.num_encoder;
+
+ if (total_objects == 0)
+ return -EINVAL;
+
+ group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL);
+ if (!group->id_list)
+ return -ENOMEM;
+
+ group->num_crtcs = 0;
+ group->num_connectors = 0;
+ group->num_encoders = 0;
+ return 0;
+}
+
+int drm_mode_group_init_legacy_group(struct drm_device *dev,
+ struct drm_mode_group *group)
+{
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ int ret;
+
+ if ((ret = drm_mode_group_init(dev, group)))
+ return ret;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+ group->id_list[group->num_crtcs++] = crtc->base.id;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ group->id_list[group->num_crtcs + group->num_encoders++] =
+ encoder->base.id;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ group->id_list[group->num_crtcs + group->num_encoders +
+ group->num_connectors++] = connector->base.id;
+
+ return 0;
+}
+
+/**
+ * drm_mode_config_cleanup - free up DRM mode_config info
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Free up all the connectors and CRTCs associated with this DRM device, then
+ * free up the framebuffers and associated buffer objects.
+ *
+ * FIXME: cleanup any dangling user buffer objects too
+ */
+void drm_mode_config_cleanup(struct drm_device *dev)
+{
+ struct drm_connector *connector, *ot;
+ struct drm_crtc *crtc, *ct;
+ struct drm_encoder *encoder, *enct;
+ struct drm_framebuffer *fb, *fbt;
+ struct drm_property *property, *pt;
+
+ list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list,
+ head) {
+ encoder->funcs->destroy(encoder);
+ }
+
+ list_for_each_entry_safe(connector, ot,
+ &dev->mode_config.connector_list, head) {
+ connector->funcs->destroy(connector);
+ }
+
+ list_for_each_entry_safe(property, pt, &dev->mode_config.property_list,
+ head) {
+ drm_property_destroy(dev, property);
+ }
+
+ list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
+ fb->funcs->destroy(fb);
+ }
+
+ list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) {
+ crtc->funcs->destroy(crtc);
+ }
+
+}
+EXPORT_SYMBOL(drm_mode_config_cleanup);
+
+/**
+ * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
+ * @out: drm_mode_modeinfo struct to return to the user
+ * @in: drm_display_mode to use
+ *
+ * LOCKING:
+ * None.
+ *
+ * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to
+ * the user.
+ */
+void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
+ struct drm_display_mode *in)
+{
+ out->clock = in->clock;
+ out->hdisplay = in->hdisplay;
+ out->hsync_start = in->hsync_start;
+ out->hsync_end = in->hsync_end;
+ out->htotal = in->htotal;
+ out->hskew = in->hskew;
+ out->vdisplay = in->vdisplay;
+ out->vsync_start = in->vsync_start;
+ out->vsync_end = in->vsync_end;
+ out->vtotal = in->vtotal;
+ out->vscan = in->vscan;
+ out->vrefresh = in->vrefresh;
+ out->flags = in->flags;
+ out->type = in->type;
+ strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
+ out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+}
+
+/**
+ * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode
+ * @out: drm_display_mode to return to the user
+ * @in: drm_mode_modeinfo to use
+ *
+ * LOCKING:
+ * None.
+ *
+ * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to
+ * the caller.
+ */
+void drm_crtc_convert_umode(struct drm_display_mode *out,
+ struct drm_mode_modeinfo *in)
+{
+ out->clock = in->clock;
+ out->hdisplay = in->hdisplay;
+ out->hsync_start = in->hsync_start;
+ out->hsync_end = in->hsync_end;
+ out->htotal = in->htotal;
+ out->hskew = in->hskew;
+ out->vdisplay = in->vdisplay;
+ out->vsync_start = in->vsync_start;
+ out->vsync_end = in->vsync_end;
+ out->vtotal = in->vtotal;
+ out->vscan = in->vscan;
+ out->vrefresh = in->vrefresh;
+ out->flags = in->flags;
+ out->type = in->type;
+ strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
+ out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+}
+
+/**
+ * drm_mode_getresources - get graphics configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Construct a set of configuration description structures and return
+ * them to the user, including CRTC, connector and framebuffer configuration.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getresources(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mode_card_res *card_res = data;
+ struct list_head *lh;
+ struct drm_framebuffer *fb;
+ struct drm_connector *connector;
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
+ int ret = 0;
+ int connector_count = 0;
+ int crtc_count = 0;
+ int fb_count = 0;
+ int encoder_count = 0;
+ int copied = 0, i;
+ uint32_t __user *fb_id;
+ uint32_t __user *crtc_id;
+ uint32_t __user *connector_id;
+ uint32_t __user *encoder_id;
+ struct drm_mode_group *mode_group;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ /*
+ * For the non-control nodes we need to limit the list of resources
+ * by IDs in the group list for this node
+ */
+ list_for_each(lh, &file_priv->fbs)
+ fb_count++;
+
+ mode_group = &file_priv->master->minor->mode_group;
+ if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+
+ list_for_each(lh, &dev->mode_config.crtc_list)
+ crtc_count++;
+
+ list_for_each(lh, &dev->mode_config.connector_list)
+ connector_count++;
+
+ list_for_each(lh, &dev->mode_config.encoder_list)
+ encoder_count++;
+ } else {
+
+ crtc_count = mode_group->num_crtcs;
+ connector_count = mode_group->num_connectors;
+ encoder_count = mode_group->num_encoders;
+ }
+
+ card_res->max_height = dev->mode_config.max_height;
+ card_res->min_height = dev->mode_config.min_height;
+ card_res->max_width = dev->mode_config.max_width;
+ card_res->min_width = dev->mode_config.min_width;
+
+ /* handle this in 4 parts */
+ /* FBs */
+ if (card_res->count_fbs >= fb_count) {
+ copied = 0;
+ fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr;
+ list_for_each_entry(fb, &file_priv->fbs, head) {
+ if (put_user(fb->base.id, fb_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ card_res->count_fbs = fb_count;
+
+ /* CRTCs */
+ if (card_res->count_crtcs >= crtc_count) {
+ copied = 0;
+ crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
+ if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list,
+ head) {
+ DRM_DEBUG("CRTC ID is %d\n", crtc->base.id);
+ if (put_user(crtc->base.id, crtc_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ } else {
+ for (i = 0; i < mode_group->num_crtcs; i++) {
+ if (put_user(mode_group->id_list[i],
+ crtc_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ card_res->count_crtcs = crtc_count;
+
+ /* Encoders */
+ if (card_res->count_encoders >= encoder_count) {
+ copied = 0;
+ encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
+ if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ list_for_each_entry(encoder,
+ &dev->mode_config.encoder_list,
+ head) {
+ DRM_DEBUG("ENCODER ID is %d\n",
+ encoder->base.id);
+ if (put_user(encoder->base.id, encoder_id +
+ copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ } else {
+ for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) {
+ if (put_user(mode_group->id_list[i],
+ encoder_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+
+ }
+ }
+ card_res->count_encoders = encoder_count;
+
+ /* Connectors */
+ if (card_res->count_connectors >= connector_count) {
+ copied = 0;
+ connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
+ if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ list_for_each_entry(connector,
+ &dev->mode_config.connector_list,
+ head) {
+ DRM_DEBUG("CONNECTOR ID is %d\n",
+ connector->base.id);
+ if (put_user(connector->base.id,
+ connector_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ } else {
+ int start = mode_group->num_crtcs +
+ mode_group->num_encoders;
+ for (i = start; i < start + mode_group->num_connectors; i++) {
+ if (put_user(mode_group->id_list[i],
+ connector_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ card_res->count_connectors = connector_count;
+
+ DRM_DEBUG("Counted %d %d %d\n", card_res->count_crtcs,
+ card_res->count_connectors, card_res->count_encoders);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_getcrtc - get CRTC configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Construct a CRTC configuration structure to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getcrtc(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_crtc *crtc_resp = data;
+ struct drm_crtc *crtc;
+ struct drm_mode_object *obj;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
+ DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ crtc_resp->x = crtc->x;
+ crtc_resp->y = crtc->y;
+ crtc_resp->gamma_size = crtc->gamma_size;
+ if (crtc->fb)
+ crtc_resp->fb_id = crtc->fb->base.id;
+ else
+ crtc_resp->fb_id = 0;
+
+ if (crtc->enabled) {
+
+ drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode);
+ crtc_resp->mode_valid = 1;
+
+ } else {
+ crtc_resp->mode_valid = 0;
+ }
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_getconnector - get connector configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Construct a connector configuration structure to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getconnector(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mode_get_connector *out_resp = data;
+ struct drm_mode_object *obj;
+ struct drm_connector *connector;
+ struct drm_display_mode *mode;
+ int mode_count = 0;
+ int props_count = 0;
+ int encoders_count = 0;
+ int ret = 0;
+ int copied = 0;
+ int i;
+ struct drm_mode_modeinfo u_mode;
+ struct drm_mode_modeinfo __user *mode_ptr;
+ uint32_t __user *prop_ptr;
+ uint64_t __user *prop_values;
+ uint32_t __user *encoder_ptr;
+
+ memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
+
+ DRM_DEBUG("connector id %d:\n", out_resp->connector_id);
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, out_resp->connector_id,
+ DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] != 0) {
+ props_count++;
+ }
+ }
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] != 0) {
+ encoders_count++;
+ }
+ }
+
+ if (out_resp->count_modes == 0) {
+ connector->funcs->fill_modes(connector,
+ dev->mode_config.max_width,
+ dev->mode_config.max_height);
+ }
+
+ /* delayed so we get modes regardless of pre-fill_modes state */
+ list_for_each_entry(mode, &connector->modes, head)
+ mode_count++;
+
+ out_resp->connector_id = connector->base.id;
+ out_resp->connector_type = connector->connector_type;
+ out_resp->connector_type_id = connector->connector_type_id;
+ out_resp->mm_width = connector->display_info.width_mm;
+ out_resp->mm_height = connector->display_info.height_mm;
+ out_resp->subpixel = connector->display_info.subpixel_order;
+ out_resp->connection = connector->status;
+ if (connector->encoder)
+ out_resp->encoder_id = connector->encoder->base.id;
+ else
+ out_resp->encoder_id = 0;
+
+ /*
+ * This ioctl is called twice, once to determine how much space is
+ * needed, and the 2nd time to fill it.
+ */
+ if ((out_resp->count_modes >= mode_count) && mode_count) {
+ copied = 0;
+ mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr;
+ list_for_each_entry(mode, &connector->modes, head) {
+ drm_crtc_convert_to_umode(&u_mode, mode);
+ if (copy_to_user(mode_ptr + copied,
+ &u_mode, sizeof(u_mode))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ out_resp->count_modes = mode_count;
+
+ if ((out_resp->count_props >= props_count) && props_count) {
+ copied = 0;
+ prop_ptr = (uint32_t *)(unsigned long)(out_resp->props_ptr);
+ prop_values = (uint64_t *)(unsigned long)(out_resp->prop_values_ptr);
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] != 0) {
+ if (put_user(connector->property_ids[i],
+ prop_ptr + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (put_user(connector->property_values[i],
+ prop_values + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ out_resp->count_props = props_count;
+
+ if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
+ copied = 0;
+ encoder_ptr = (uint32_t *)(unsigned long)(out_resp->encoders_ptr);
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] != 0) {
+ if (put_user(connector->encoder_ids[i],
+ encoder_ptr + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ out_resp->count_encoders = encoders_count;
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+int drm_mode_getencoder(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mode_get_encoder *enc_resp = data;
+ struct drm_mode_object *obj;
+ struct drm_encoder *encoder;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, enc_resp->encoder_id,
+ DRM_MODE_OBJECT_ENCODER);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ encoder = obj_to_encoder(obj);
+
+ if (encoder->crtc)
+ enc_resp->crtc_id = encoder->crtc->base.id;
+ else
+ enc_resp->crtc_id = 0;
+ enc_resp->encoder_type = encoder->encoder_type;
+ enc_resp->encoder_id = encoder->base.id;
+ enc_resp->possible_crtcs = encoder->possible_crtcs;
+ enc_resp->possible_clones = encoder->possible_clones;
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_setcrtc - set CRTC configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Build a new CRTC configuration based on user request.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_setcrtc(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_mode_crtc *crtc_req = data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc, *crtcfb;
+ struct drm_connector **connector_set = NULL, *connector;
+ struct drm_framebuffer *fb = NULL;
+ struct drm_display_mode *mode = NULL;
+ struct drm_mode_set set;
+ uint32_t __user *set_connectors_ptr;
+ int ret = 0;
+ int i;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, crtc_req->crtc_id,
+ DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req->crtc_id);
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ if (crtc_req->mode_valid) {
+ /* If we have a mode we need a framebuffer. */
+ /* If we pass -1, set the mode with the currently bound fb */
+ if (crtc_req->fb_id == -1) {
+ list_for_each_entry(crtcfb,
+ &dev->mode_config.crtc_list, head) {
+ if (crtcfb == crtc) {
+ DRM_DEBUG("Using current fb for setmode\n");
+ fb = crtc->fb;
+ }
+ }
+ } else {
+ obj = drm_mode_object_find(dev, crtc_req->fb_id,
+ DRM_MODE_OBJECT_FB);
+ if (!obj) {
+ DRM_DEBUG("Unknown FB ID%d\n", crtc_req->fb_id);
+ ret = -EINVAL;
+ goto out;
+ }
+ fb = obj_to_fb(obj);
+ }
+
+ mode = drm_mode_create(dev);
+ drm_crtc_convert_umode(mode, &crtc_req->mode);
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ }
+
+ if (crtc_req->count_connectors == 0 && mode) {
+ DRM_DEBUG("Count connectors is 0 but mode set\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (crtc_req->count_connectors > 0 && !mode && !fb) {
+ DRM_DEBUG("Count connectors is %d but no mode or fb set\n",
+ crtc_req->count_connectors);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (crtc_req->count_connectors > 0) {
+ u32 out_id;
+
+ /* Avoid unbounded kernel memory allocation */
+ if (crtc_req->count_connectors > config->num_connector) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ connector_set = kmalloc(crtc_req->count_connectors *
+ sizeof(struct drm_connector *),
+ GFP_KERNEL);
+ if (!connector_set) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < crtc_req->count_connectors; i++) {
+ set_connectors_ptr = (uint32_t *)(unsigned long)crtc_req->set_connectors_ptr;
+ if (get_user(out_id, &set_connectors_ptr[i])) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ obj = drm_mode_object_find(dev, out_id,
+ DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ DRM_DEBUG("Connector id %d unknown\n", out_id);
+ ret = -EINVAL;
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ connector_set[i] = connector;
+ }
+ }
+
+ set.crtc = crtc;
+ set.x = crtc_req->x;
+ set.y = crtc_req->y;
+ set.mode = mode;
+ set.connectors = connector_set;
+ set.num_connectors = crtc_req->count_connectors;
+ set.fb =fb;
+ ret = crtc->funcs->set_config(&set);
+
+out:
+ kfree(connector_set);
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+int drm_mode_cursor_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_cursor *req = data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc;
+ int ret = 0;
+
+ DRM_DEBUG("\n");
+
+ if (!req->flags) {
+ DRM_ERROR("no operation set\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ DRM_DEBUG("Unknown CRTC ID %d\n", req->crtc_id);
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ if (req->flags & DRM_MODE_CURSOR_BO) {
+ if (!crtc->funcs->cursor_set) {
+ DRM_ERROR("crtc does not support cursor\n");
+ ret = -ENXIO;
+ goto out;
+ }
+ /* Turns off the cursor if handle is 0 */
+ ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle,
+ req->width, req->height);
+ }
+
+ if (req->flags & DRM_MODE_CURSOR_MOVE) {
+ if (crtc->funcs->cursor_move) {
+ ret = crtc->funcs->cursor_move(crtc, req->x, req->y);
+ } else {
+ DRM_ERROR("crtc does not support cursor\n");
+ ret = -EFAULT;
+ goto out;
+ }
+ }
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_addfb - add an FB to the graphics configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Add a new FB to the specified CRTC, given a user request.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_addfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_fb_cmd *r = data;
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_framebuffer *fb;
+ int ret = 0;
+
+ if ((config->min_width > r->width) || (r->width > config->max_width)) {
+ DRM_ERROR("mode new framebuffer width not within limits\n");
+ return -EINVAL;
+ }
+ if ((config->min_height > r->height) || (r->height > config->max_height)) {
+ DRM_ERROR("mode new framebuffer height not within limits\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ /* TODO check buffer is sufficently large */
+ /* TODO setup destructor callback */
+
+ fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
+ if (!fb) {
+ DRM_ERROR("could not create framebuffer\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ r->fb_id = fb->base.id;
+ list_add(&fb->filp_head, &file_priv->fbs);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_rmfb - remove an FB from the configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Remove the FB specified by the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_rmfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_object *obj;
+ struct drm_framebuffer *fb = NULL;
+ struct drm_framebuffer *fbl = NULL;
+ uint32_t *id = data;
+ int ret = 0;
+ int found = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB);
+ /* TODO check that we realy get a framebuffer back. */
+ if (!obj) {
+ DRM_ERROR("mode invalid framebuffer id\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ fb = obj_to_fb(obj);
+
+ list_for_each_entry(fbl, &file_priv->fbs, filp_head)
+ if (fb == fbl)
+ found = 1;
+
+ if (!found) {
+ DRM_ERROR("tried to remove a fb that we didn't own\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* TODO release all crtc connected to the framebuffer */
+ /* TODO unhock the destructor from the buffer object */
+
+ list_del(&fb->filp_head);
+ fb->funcs->destroy(fb);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_getfb - get FB info
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Lookup the FB given its ID and return info about it.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_fb_cmd *r = data;
+ struct drm_mode_object *obj;
+ struct drm_framebuffer *fb;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB);
+ if (!obj) {
+ DRM_ERROR("invalid framebuffer id\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ fb = obj_to_fb(obj);
+
+ r->height = fb->height;
+ r->width = fb->width;
+ r->depth = fb->depth;
+ r->bpp = fb->bits_per_pixel;
+ r->pitch = fb->pitch;
+ fb->funcs->create_handle(fb, file_priv, &r->handle);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_fb_release - remove and free the FBs on this file
+ * @filp: file * from the ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Destroy all the FBs associated with @filp.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+void drm_fb_release(struct file *filp)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->minor->dev;
+ struct drm_framebuffer *fb, *tfb;
+
+ mutex_lock(&dev->mode_config.mutex);
+ list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
+ list_del(&fb->filp_head);
+ fb->funcs->destroy(fb);
+ }
+ mutex_unlock(&dev->mode_config.mutex);
+}
+
+/**
+ * drm_mode_attachmode - add a mode to the user mode list
+ * @dev: DRM device
+ * @connector: connector to add the mode to
+ * @mode: mode to add
+ *
+ * Add @mode to @connector's user mode list.
+ */
+static int drm_mode_attachmode(struct drm_device *dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ int ret = 0;
+
+ list_add_tail(&mode->head, &connector->user_modes);
+ return ret;
+}
+
+int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc,
+ struct drm_display_mode *mode)
+{
+ struct drm_connector *connector;
+ int ret = 0;
+ struct drm_display_mode *dup_mode;
+ int need_dup = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (!connector->encoder)
+ break;
+ if (connector->encoder->crtc == crtc) {
+ if (need_dup)
+ dup_mode = drm_mode_duplicate(dev, mode);
+ else
+ dup_mode = mode;
+ ret = drm_mode_attachmode(dev, connector, dup_mode);
+ if (ret)
+ return ret;
+ need_dup = 1;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_attachmode_crtc);
+
+static int drm_mode_detachmode(struct drm_device *dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ int found = 0;
+ int ret = 0;
+ struct drm_display_mode *match_mode, *t;
+
+ list_for_each_entry_safe(match_mode, t, &connector->user_modes, head) {
+ if (drm_mode_equal(match_mode, mode)) {
+ list_del(&match_mode->head);
+ drm_mode_destroy(dev, match_mode);
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ ret = -EINVAL;
+
+ return ret;
+}
+
+int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode)
+{
+ struct drm_connector *connector;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ drm_mode_detachmode(dev, connector, mode);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_detachmode_crtc);
+
+/**
+ * drm_fb_attachmode - Attach a user mode to an connector
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * This attaches a user specified mode to an connector.
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_attachmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_mode_cmd *mode_cmd = data;
+ struct drm_connector *connector;
+ struct drm_display_mode *mode;
+ struct drm_mode_object *obj;
+ struct drm_mode_modeinfo *umode = &mode_cmd->mode;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ mode = drm_mode_create(dev);
+ if (!mode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ drm_crtc_convert_umode(mode, umode);
+
+ ret = drm_mode_attachmode(dev, connector, mode);
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+
+/**
+ * drm_fb_detachmode - Detach a user specified mode from an connector
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_detachmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_object *obj;
+ struct drm_mode_mode_cmd *mode_cmd = data;
+ struct drm_connector *connector;
+ struct drm_display_mode mode;
+ struct drm_mode_modeinfo *umode = &mode_cmd->mode;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ drm_crtc_convert_umode(&mode, umode);
+ ret = drm_mode_detachmode(dev, connector, &mode);
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+struct drm_property *drm_property_create(struct drm_device *dev, int flags,
+ const char *name, int num_values)
+{
+ struct drm_property *property = NULL;
+
+ property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
+ if (!property)
+ return NULL;
+
+ if (num_values) {
+ property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
+ if (!property->values)
+ goto fail;
+ }
+
+ drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
+ property->flags = flags;
+ property->num_values = num_values;
+ INIT_LIST_HEAD(&property->enum_blob_list);
+
+ if (name)
+ strncpy(property->name, name, DRM_PROP_NAME_LEN);
+
+ list_add_tail(&property->head, &dev->mode_config.property_list);
+ return property;
+fail:
+ kfree(property);
+ return NULL;
+}
+EXPORT_SYMBOL(drm_property_create);
+
+int drm_property_add_enum(struct drm_property *property, int index,
+ uint64_t value, const char *name)
+{
+ struct drm_property_enum *prop_enum;
+
+ if (!(property->flags & DRM_MODE_PROP_ENUM))
+ return -EINVAL;
+
+ if (!list_empty(&property->enum_blob_list)) {
+ list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
+ if (prop_enum->value == value) {
+ strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
+ prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
+ return 0;
+ }
+ }
+ }
+
+ prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL);
+ if (!prop_enum)
+ return -ENOMEM;
+
+ strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
+ prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
+ prop_enum->value = value;
+
+ property->values[index] = value;
+ list_add_tail(&prop_enum->head, &property->enum_blob_list);
+ return 0;
+}
+EXPORT_SYMBOL(drm_property_add_enum);
+
+void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
+{
+ struct drm_property_enum *prop_enum, *pt;
+
+ list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) {
+ list_del(&prop_enum->head);
+ kfree(prop_enum);
+ }
+
+ if (property->num_values)
+ kfree(property->values);
+ drm_mode_object_put(dev, &property->base);
+ list_del(&property->head);
+ kfree(property);
+}
+EXPORT_SYMBOL(drm_property_destroy);
+
+int drm_connector_attach_property(struct drm_connector *connector,
+ struct drm_property *property, uint64_t init_val)
+{
+ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] == 0) {
+ connector->property_ids[i] = property->base.id;
+ connector->property_values[i] = init_val;
+ break;
+ }
+ }
+
+ if (i == DRM_CONNECTOR_MAX_PROPERTY)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_property);
+
+int drm_connector_property_set_value(struct drm_connector *connector,
+ struct drm_property *property, uint64_t value)
+{
+ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] == property->base.id) {
+ connector->property_values[i] = value;
+ break;
+ }
+ }
+
+ if (i == DRM_CONNECTOR_MAX_PROPERTY)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_property_set_value);
+
+int drm_connector_property_get_value(struct drm_connector *connector,
+ struct drm_property *property, uint64_t *val)
+{
+ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] == property->base.id) {
+ *val = connector->property_values[i];
+ break;
+ }
+ }
+
+ if (i == DRM_CONNECTOR_MAX_PROPERTY)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_property_get_value);
+
+int drm_mode_getproperty_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_object *obj;
+ struct drm_mode_get_property *out_resp = data;
+ struct drm_property *property;
+ int enum_count = 0;
+ int blob_count = 0;
+ int value_count = 0;
+ int ret = 0, i;
+ int copied;
+ struct drm_property_enum *prop_enum;
+ struct drm_mode_property_enum __user *enum_ptr;
+ struct drm_property_blob *prop_blob;
+ uint32_t *blob_id_ptr;
+ uint64_t __user *values_ptr;
+ uint32_t __user *blob_length_ptr;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
+ if (!obj) {
+ ret = -EINVAL;
+ goto done;
+ }
+ property = obj_to_property(obj);
+
+ if (property->flags & DRM_MODE_PROP_ENUM) {
+ list_for_each_entry(prop_enum, &property->enum_blob_list, head)
+ enum_count++;
+ } else if (property->flags & DRM_MODE_PROP_BLOB) {
+ list_for_each_entry(prop_blob, &property->enum_blob_list, head)
+ blob_count++;
+ }
+
+ value_count = property->num_values;
+
+ strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN);
+ out_resp->name[DRM_PROP_NAME_LEN-1] = 0;
+ out_resp->flags = property->flags;
+
+ if ((out_resp->count_values >= value_count) && value_count) {
+ values_ptr = (uint64_t *)(unsigned long)out_resp->values_ptr;
+ for (i = 0; i < value_count; i++) {
+ if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ }
+ out_resp->count_values = value_count;
+
+ if (property->flags & DRM_MODE_PROP_ENUM) {
+ if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
+ copied = 0;
+ enum_ptr = (struct drm_mode_property_enum *)(unsigned long)out_resp->enum_blob_ptr;
+ list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
+
+ if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (copy_to_user(&enum_ptr[copied].name,
+ &prop_enum->name, DRM_PROP_NAME_LEN)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ copied++;
+ }
+ }
+ out_resp->count_enum_blobs = enum_count;
+ }
+
+ if (property->flags & DRM_MODE_PROP_BLOB) {
+ if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
+ copied = 0;
+ blob_id_ptr = (uint32_t *)(unsigned long)out_resp->enum_blob_ptr;
+ blob_length_ptr = (uint32_t *)(unsigned long)out_resp->values_ptr;
+
+ list_for_each_entry(prop_blob, &property->enum_blob_list, head) {
+ if (put_user(prop_blob->base.id, blob_id_ptr + copied)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (put_user(prop_blob->length, blob_length_ptr + copied)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ copied++;
+ }
+ }
+ out_resp->count_enum_blobs = blob_count;
+ }
+done:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length,
+ void *data)
+{
+ struct drm_property_blob *blob;
+
+ if (!length || !data)
+ return NULL;
+
+ blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
+ if (!blob)
+ return NULL;
+
+ blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob));
+ blob->length = length;
+
+ memcpy(blob->data, data, length);
+
+ drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
+
+ list_add_tail(&blob->head, &dev->mode_config.property_blob_list);
+ return blob;
+}
+
+static void drm_property_destroy_blob(struct drm_device *dev,
+ struct drm_property_blob *blob)
+{
+ drm_mode_object_put(dev, &blob->base);
+ list_del(&blob->head);
+ kfree(blob);
+}
+
+int drm_mode_getblob_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_object *obj;
+ struct drm_mode_get_blob *out_resp = data;
+ struct drm_property_blob *blob;
+ int ret = 0;
+ void *blob_ptr;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
+ if (!obj) {
+ ret = -EINVAL;
+ goto done;
+ }
+ blob = obj_to_blob(obj);
+
+ if (out_resp->length == blob->length) {
+ blob_ptr = (void *)(unsigned long)out_resp->data;
+ if (copy_to_user(blob_ptr, blob->data, blob->length)){
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ out_resp->length = blob->length;
+
+done:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+int drm_mode_connector_update_edid_property(struct drm_connector *connector,
+ struct edid *edid)
+{
+ struct drm_device *dev = connector->dev;
+ int ret = 0;
+
+ if (connector->edid_blob_ptr)
+ drm_property_destroy_blob(dev, connector->edid_blob_ptr);
+
+ /* Delete edid, when there is none. */
+ if (!edid) {
+ connector->edid_blob_ptr = NULL;
+ ret = drm_connector_property_set_value(connector, dev->mode_config.edid_property, 0);
+ return ret;
+ }
+
+ connector->edid_blob_ptr = drm_property_create_blob(connector->dev, 128, edid);
+
+ ret = drm_connector_property_set_value(connector,
+ dev->mode_config.edid_property,
+ connector->edid_blob_ptr->base.id);
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
+
+int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_connector_set_property *out_resp = data;
+ struct drm_mode_object *obj;
+ struct drm_property *property;
+ struct drm_connector *connector;
+ int ret = -EINVAL;
+ int i;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] == out_resp->prop_id)
+ break;
+ }
+
+ if (i == DRM_CONNECTOR_MAX_PROPERTY) {
+ goto out;
+ }
+
+ obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
+ if (!obj) {
+ goto out;
+ }
+ property = obj_to_property(obj);
+
+ if (property->flags & DRM_MODE_PROP_IMMUTABLE)
+ goto out;
+
+ if (property->flags & DRM_MODE_PROP_RANGE) {
+ if (out_resp->value < property->values[0])
+ goto out;
+
+ if (out_resp->value > property->values[1])
+ goto out;
+ } else {
+ int found = 0;
+ for (i = 0; i < property->num_values; i++) {
+ if (property->values[i] == out_resp->value) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ goto out;
+ }
+ }
+
+ if (connector->funcs->set_property)
+ ret = connector->funcs->set_property(connector, property, out_resp->value);
+
+ /* store the property value if succesful */
+ if (!ret)
+ drm_connector_property_set_value(connector, property, out_resp->value);
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+int drm_mode_connector_attach_encoder(struct drm_connector *connector,
+ struct drm_encoder *encoder)
+{
+ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] == 0) {
+ connector->encoder_ids[i] = encoder->base.id;
+ return 0;
+ }
+ }
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
+
+void drm_mode_connector_detach_encoder(struct drm_connector *connector,
+ struct drm_encoder *encoder)
+{
+ int i;
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] == encoder->base.id) {
+ connector->encoder_ids[i] = 0;
+ if (connector->encoder == encoder)
+ connector->encoder = NULL;
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL(drm_mode_connector_detach_encoder);
+
+bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
+ int gamma_size)
+{
+ crtc->gamma_size = gamma_size;
+
+ crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL);
+ if (!crtc->gamma_store) {
+ crtc->gamma_size = 0;
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
+
+int drm_mode_gamma_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_crtc_lut *crtc_lut = data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc;
+ void *r_base, *g_base, *b_base;
+ int size;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ /* memcpy into gamma store */
+ if (crtc_lut->gamma_size != crtc->gamma_size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ size = crtc_lut->gamma_size * (sizeof(uint16_t));
+ r_base = crtc->gamma_store;
+ if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ g_base = r_base + size;
+ if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ b_base = g_base + size;
+ if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+
+}
+
+int drm_mode_gamma_get_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_crtc_lut *crtc_lut = data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc;
+ void *r_base, *g_base, *b_base;
+ int size;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ /* memcpy into gamma store */
+ if (crtc_lut->gamma_size != crtc->gamma_size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ size = crtc_lut->gamma_size * (sizeof(uint16_t));
+ r_base = crtc->gamma_store;
+ if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ g_base = r_base + size;
+ if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ b_base = g_base + size;
+ if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
new file mode 100644
index 000000000000..d8a982b71296
--- /dev/null
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -0,0 +1,826 @@
+/*
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ *
+ * DRM core CRTC related functions
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Keith Packard
+ * Eric Anholt <eric@anholt.net>
+ * Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+/*
+ * Detailed mode info for 800x600@60Hz
+ */
+static struct drm_display_mode std_mode[] = {
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DEFAULT, 40000, 800, 840,
+ 968, 1056, 0, 600, 601, 605, 628, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+};
+
+/**
+ * drm_helper_probe_connector_modes - get complete set of display modes
+ * @dev: DRM device
+ * @maxX: max width for modes
+ * @maxY: max height for modes
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Based on @dev's mode_config layout, scan all the connectors and try to detect
+ * modes on them. Modes will first be added to the connector's probed_modes
+ * list, then culled (based on validity and the @maxX, @maxY parameters) and
+ * put into the normal modes list.
+ *
+ * Intended to be used either at bootup time or when major configuration
+ * changes have occurred.
+ *
+ * FIXME: take into account monitor limits
+ */
+void drm_helper_probe_single_connector_modes(struct drm_connector *connector,
+ uint32_t maxX, uint32_t maxY)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_display_mode *mode, *t;
+ struct drm_connector_helper_funcs *connector_funcs =
+ connector->helper_private;
+ int ret;
+
+ DRM_DEBUG("%s\n", drm_get_connector_name(connector));
+ /* set all modes to the unverified state */
+ list_for_each_entry_safe(mode, t, &connector->modes, head)
+ mode->status = MODE_UNVERIFIED;
+
+ connector->status = connector->funcs->detect(connector);
+
+ if (connector->status == connector_status_disconnected) {
+ DRM_DEBUG("%s is disconnected\n",
+ drm_get_connector_name(connector));
+ /* TODO set EDID to NULL */
+ return;
+ }
+
+ ret = (*connector_funcs->get_modes)(connector);
+
+ if (ret) {
+ drm_mode_connector_list_update(connector);
+ }
+
+ if (maxX && maxY)
+ drm_mode_validate_size(dev, &connector->modes, maxX,
+ maxY, 0);
+ list_for_each_entry_safe(mode, t, &connector->modes, head) {
+ if (mode->status == MODE_OK)
+ mode->status = connector_funcs->mode_valid(connector,
+ mode);
+ }
+
+
+ drm_mode_prune_invalid(dev, &connector->modes, true);
+
+ if (list_empty(&connector->modes)) {
+ struct drm_display_mode *stdmode;
+
+ DRM_DEBUG("No valid modes on %s\n",
+ drm_get_connector_name(connector));
+
+ /* Should we do this here ???
+ * When no valid EDID modes are available we end up
+ * here and bailed in the past, now we add a standard
+ * 640x480@60Hz mode and carry on.
+ */
+ stdmode = drm_mode_duplicate(dev, &std_mode[0]);
+ drm_mode_probed_add(connector, stdmode);
+ drm_mode_list_concat(&connector->probed_modes,
+ &connector->modes);
+
+ DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n",
+ drm_get_connector_name(connector));
+ }
+
+ drm_mode_sort(&connector->modes);
+
+ DRM_DEBUG("Probed modes for %s\n", drm_get_connector_name(connector));
+ list_for_each_entry_safe(mode, t, &connector->modes, head) {
+ mode->vrefresh = drm_mode_vrefresh(mode);
+
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ drm_mode_debug_printmodeline(mode);
+ }
+}
+EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
+
+void drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX,
+ uint32_t maxY)
+{
+ struct drm_connector *connector;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ drm_helper_probe_single_connector_modes(connector, maxX, maxY);
+ }
+}
+EXPORT_SYMBOL(drm_helper_probe_connector_modes);
+
+
+/**
+ * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config
+ * @crtc: CRTC to check
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Walk @crtc's DRM device's mode_config and see if it's in use.
+ *
+ * RETURNS:
+ * True if @crtc is part of the mode_config, false otherwise.
+ */
+bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
+{
+ struct drm_encoder *encoder;
+ struct drm_device *dev = crtc->dev;
+ /* FIXME: Locking around list access? */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ if (encoder->crtc == crtc)
+ return true;
+ return false;
+}
+EXPORT_SYMBOL(drm_helper_crtc_in_use);
+
+/**
+ * drm_disable_unused_functions - disable unused objects
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled
+ * by calling its dpms function, which should power it off.
+ */
+void drm_helper_disable_unused_functions(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+ struct drm_encoder_helper_funcs *encoder_funcs;
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ encoder_funcs = encoder->helper_private;
+ if (!encoder->crtc)
+ (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
+ }
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ crtc->enabled = drm_helper_crtc_in_use(crtc);
+ if (!crtc->enabled) {
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+ crtc->fb = NULL;
+ }
+ }
+}
+EXPORT_SYMBOL(drm_helper_disable_unused_functions);
+
+static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *connector, int width, int height)
+{
+ struct drm_display_mode *mode;
+
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (drm_mode_width(mode) > width ||
+ drm_mode_height(mode) > height)
+ continue;
+ if (mode->type & DRM_MODE_TYPE_PREFERRED)
+ return mode;
+ }
+ return NULL;
+}
+
+static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
+{
+ bool enable;
+
+ if (strict) {
+ enable = connector->status == connector_status_connected;
+ } else {
+ enable = connector->status != connector_status_disconnected;
+ }
+ return enable;
+}
+
+static void drm_enable_connectors(struct drm_device *dev, bool *enabled)
+{
+ bool any_enabled = false;
+ struct drm_connector *connector;
+ int i = 0;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ enabled[i] = drm_connector_enabled(connector, true);
+ any_enabled |= enabled[i];
+ i++;
+ }
+
+ if (any_enabled)
+ return;
+
+ i = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ enabled[i] = drm_connector_enabled(connector, false);
+ i++;
+ }
+}
+
+static bool drm_target_preferred(struct drm_device *dev,
+ struct drm_display_mode **modes,
+ bool *enabled, int width, int height)
+{
+ struct drm_connector *connector;
+ int i = 0;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+
+ if (enabled[i] == false) {
+ i++;
+ continue;
+ }
+
+ modes[i] = drm_has_preferred_mode(connector, width, height);
+ if (!modes[i]) {
+ list_for_each_entry(modes[i], &connector->modes, head)
+ break;
+ }
+ i++;
+ }
+ return true;
+}
+
+static int drm_pick_crtcs(struct drm_device *dev,
+ struct drm_crtc **best_crtcs,
+ struct drm_display_mode **modes,
+ int n, int width, int height)
+{
+ int c, o;
+ struct drm_connector *connector;
+ struct drm_connector_helper_funcs *connector_funcs;
+ struct drm_encoder *encoder;
+ struct drm_crtc *best_crtc;
+ int my_score, best_score, score;
+ struct drm_crtc **crtcs, *crtc;
+
+ if (n == dev->mode_config.num_connector)
+ return 0;
+ c = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (c == n)
+ break;
+ c++;
+ }
+
+ best_crtcs[n] = NULL;
+ best_crtc = NULL;
+ best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height);
+ if (modes[n] == NULL)
+ return best_score;
+
+ crtcs = kmalloc(dev->mode_config.num_connector *
+ sizeof(struct drm_crtc *), GFP_KERNEL);
+ if (!crtcs)
+ return best_score;
+
+ my_score = 1;
+ if (connector->status == connector_status_connected)
+ my_score++;
+ if (drm_has_preferred_mode(connector, width, height))
+ my_score++;
+
+ connector_funcs = connector->helper_private;
+ encoder = connector_funcs->best_encoder(connector);
+ if (!encoder)
+ goto out;
+
+ connector->encoder = encoder;
+
+ /* select a crtc for this connector and then attempt to configure
+ remaining connectors */
+ c = 0;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+
+ if ((connector->encoder->possible_crtcs & (1 << c)) == 0) {
+ c++;
+ continue;
+ }
+
+ for (o = 0; o < n; o++)
+ if (best_crtcs[o] == crtc)
+ break;
+
+ if (o < n) {
+ /* ignore cloning for now */
+ c++;
+ continue;
+ }
+
+ crtcs[n] = crtc;
+ memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *));
+ score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1,
+ width, height);
+ if (score > best_score) {
+ best_crtc = crtc;
+ best_score = score;
+ memcpy(best_crtcs, crtcs,
+ dev->mode_config.num_connector *
+ sizeof(struct drm_crtc *));
+ }
+ c++;
+ }
+out:
+ kfree(crtcs);
+ return best_score;
+}
+
+static void drm_setup_crtcs(struct drm_device *dev)
+{
+ struct drm_crtc **crtcs;
+ struct drm_display_mode **modes;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ bool *enabled;
+ int width, height;
+ int i, ret;
+
+ width = dev->mode_config.max_width;
+ height = dev->mode_config.max_height;
+
+ /* clean out all the encoder/crtc combos */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ encoder->crtc = NULL;
+ }
+
+ crtcs = kcalloc(dev->mode_config.num_connector,
+ sizeof(struct drm_crtc *), GFP_KERNEL);
+ modes = kcalloc(dev->mode_config.num_connector,
+ sizeof(struct drm_display_mode *), GFP_KERNEL);
+ enabled = kcalloc(dev->mode_config.num_connector,
+ sizeof(bool), GFP_KERNEL);
+
+ drm_enable_connectors(dev, enabled);
+
+ ret = drm_target_preferred(dev, modes, enabled, width, height);
+ if (!ret)
+ DRM_ERROR("Unable to find initial modes\n");
+
+ drm_pick_crtcs(dev, crtcs, modes, 0, width, height);
+
+ i = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct drm_display_mode *mode = modes[i];
+ struct drm_crtc *crtc = crtcs[i];
+
+ if (connector->encoder == NULL) {
+ i++;
+ continue;
+ }
+
+ if (mode && crtc) {
+ crtc->desired_mode = mode;
+ connector->encoder->crtc = crtc;
+ } else
+ connector->encoder->crtc = NULL;
+ i++;
+ }
+
+ kfree(crtcs);
+ kfree(modes);
+ kfree(enabled);
+}
+/**
+ * drm_crtc_set_mode - set a mode
+ * @crtc: CRTC to program
+ * @mode: mode to use
+ * @x: width of mode
+ * @y: height of mode
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance
+ * to fixup or reject the mode prior to trying to set it.
+ *
+ * RETURNS:
+ * True if the mode was set successfully, or false otherwise.
+ */
+bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_display_mode *adjusted_mode, saved_mode;
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ struct drm_encoder_helper_funcs *encoder_funcs;
+ int saved_x, saved_y;
+ struct drm_encoder *encoder;
+ bool ret = true;
+
+ adjusted_mode = drm_mode_duplicate(dev, mode);
+
+ crtc->enabled = drm_helper_crtc_in_use(crtc);
+
+ if (!crtc->enabled)
+ return true;
+
+ saved_mode = crtc->mode;
+ saved_x = crtc->x;
+ saved_y = crtc->y;
+
+ /* Update crtc values up front so the driver can rely on them for mode
+ * setting.
+ */
+ crtc->mode = *mode;
+ crtc->x = x;
+ crtc->y = y;
+
+ if (drm_mode_equal(&saved_mode, &crtc->mode)) {
+ if (saved_x != crtc->x || saved_y != crtc->y) {
+ crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y,
+ old_fb);
+ goto done;
+ }
+ }
+
+ /* Pass our mode to the connectors and the CRTC to give them a chance to
+ * adjust it according to limitations or connector properties, and also
+ * a chance to reject the mode entirely.
+ */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+ if (encoder->crtc != crtc)
+ continue;
+ encoder_funcs = encoder->helper_private;
+ if (!(ret = encoder_funcs->mode_fixup(encoder, mode,
+ adjusted_mode))) {
+ goto done;
+ }
+ }
+
+ if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) {
+ goto done;
+ }
+
+ /* Prepare the encoders and CRTCs before setting the mode. */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+ if (encoder->crtc != crtc)
+ continue;
+ encoder_funcs = encoder->helper_private;
+ /* Disable the encoders as the first thing we do. */
+ encoder_funcs->prepare(encoder);
+ }
+
+ crtc_funcs->prepare(crtc);
+
+ /* Set up the DPLL and any encoders state that needs to adjust or depend
+ * on the DPLL.
+ */
+ crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb);
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+ if (encoder->crtc != crtc)
+ continue;
+
+ DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder),
+ mode->name, mode->base.id);
+ encoder_funcs = encoder->helper_private;
+ encoder_funcs->mode_set(encoder, mode, adjusted_mode);
+ }
+
+ /* Now enable the clocks, plane, pipe, and connectors that we set up. */
+ crtc_funcs->commit(crtc);
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+ if (encoder->crtc != crtc)
+ continue;
+
+ encoder_funcs = encoder->helper_private;
+ encoder_funcs->commit(encoder);
+
+ }
+
+ /* XXX free adjustedmode */
+ drm_mode_destroy(dev, adjusted_mode);
+ /* FIXME: add subpixel order */
+done:
+ if (!ret) {
+ crtc->mode = saved_mode;
+ crtc->x = saved_x;
+ crtc->y = saved_y;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_crtc_helper_set_mode);
+
+
+/**
+ * drm_crtc_helper_set_config - set a new config from userspace
+ * @crtc: CRTC to setup
+ * @crtc_info: user provided configuration
+ * @new_mode: new mode to set
+ * @connector_set: set of connectors for the new config
+ * @fb: new framebuffer
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Setup a new configuration, provided by the user in @crtc_info, and enable
+ * it.
+ *
+ * RETURNS:
+ * Zero. (FIXME)
+ */
+int drm_crtc_helper_set_config(struct drm_mode_set *set)
+{
+ struct drm_device *dev;
+ struct drm_crtc **save_crtcs, *new_crtc;
+ struct drm_encoder **save_encoders, *new_encoder;
+ struct drm_framebuffer *old_fb;
+ bool save_enabled;
+ bool changed = false;
+ bool flip_or_move = false;
+ struct drm_connector *connector;
+ int count = 0, ro, fail = 0;
+ struct drm_crtc_helper_funcs *crtc_funcs;
+ int ret = 0;
+
+ DRM_DEBUG("\n");
+
+ if (!set)
+ return -EINVAL;
+
+ if (!set->crtc)
+ return -EINVAL;
+
+ if (!set->crtc->helper_private)
+ return -EINVAL;
+
+ crtc_funcs = set->crtc->helper_private;
+
+ DRM_DEBUG("crtc: %p %d fb: %p connectors: %p num_connectors: %d (x, y) (%i, %i)\n",
+ set->crtc, set->crtc->base.id, set->fb, set->connectors,
+ (int)set->num_connectors, set->x, set->y);
+
+ dev = set->crtc->dev;
+
+ /* save previous config */
+ save_enabled = set->crtc->enabled;
+
+ /* this is meant to be num_connector not num_crtc */
+ save_crtcs = kzalloc(dev->mode_config.num_connector *
+ sizeof(struct drm_crtc *), GFP_KERNEL);
+ if (!save_crtcs)
+ return -ENOMEM;
+
+ save_encoders = kzalloc(dev->mode_config.num_connector *
+ sizeof(struct drm_encoders *), GFP_KERNEL);
+ if (!save_encoders) {
+ kfree(save_crtcs);
+ return -ENOMEM;
+ }
+
+ /* We should be able to check here if the fb has the same properties
+ * and then just flip_or_move it */
+ if (set->crtc->fb != set->fb) {
+ /* if we have no fb then its a change not a flip */
+ if (set->crtc->fb == NULL)
+ changed = true;
+ else
+ flip_or_move = true;
+ }
+
+ if (set->x != set->crtc->x || set->y != set->crtc->y)
+ flip_or_move = true;
+
+ if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) {
+ DRM_DEBUG("modes are different\n");
+ drm_mode_debug_printmodeline(&set->crtc->mode);
+ drm_mode_debug_printmodeline(set->mode);
+ changed = true;
+ }
+
+ /* a) traverse passed in connector list and get encoders for them */
+ count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct drm_connector_helper_funcs *connector_funcs =
+ connector->helper_private;
+ save_encoders[count++] = connector->encoder;
+ new_encoder = connector->encoder;
+ for (ro = 0; ro < set->num_connectors; ro++) {
+ if (set->connectors[ro] == connector) {
+ new_encoder = connector_funcs->best_encoder(connector);
+ /* if we can't get an encoder for a connector
+ we are setting now - then fail */
+ if (new_encoder == NULL)
+ /* don't break so fail path works correct */
+ fail = 1;
+ break;
+ }
+ }
+
+ if (new_encoder != connector->encoder) {
+ changed = true;
+ connector->encoder = new_encoder;
+ }
+ }
+
+ if (fail) {
+ ret = -EINVAL;
+ goto fail_no_encoder;
+ }
+
+ count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (!connector->encoder)
+ continue;
+
+ save_crtcs[count++] = connector->encoder->crtc;
+
+ if (connector->encoder->crtc == set->crtc)
+ new_crtc = NULL;
+ else
+ new_crtc = connector->encoder->crtc;
+
+ for (ro = 0; ro < set->num_connectors; ro++) {
+ if (set->connectors[ro] == connector)
+ new_crtc = set->crtc;
+ }
+ if (new_crtc != connector->encoder->crtc) {
+ changed = true;
+ connector->encoder->crtc = new_crtc;
+ }
+ }
+
+ /* mode_set_base is not a required function */
+ if (flip_or_move && !crtc_funcs->mode_set_base)
+ changed = true;
+
+ if (changed) {
+ old_fb = set->crtc->fb;
+ set->crtc->fb = set->fb;
+ set->crtc->enabled = (set->mode != NULL);
+ if (set->mode != NULL) {
+ DRM_DEBUG("attempting to set mode from userspace\n");
+ drm_mode_debug_printmodeline(set->mode);
+ if (!drm_crtc_helper_set_mode(set->crtc, set->mode,
+ set->x, set->y,
+ old_fb)) {
+ ret = -EINVAL;
+ goto fail_set_mode;
+ }
+ /* TODO are these needed? */
+ set->crtc->desired_x = set->x;
+ set->crtc->desired_y = set->y;
+ set->crtc->desired_mode = set->mode;
+ }
+ drm_helper_disable_unused_functions(dev);
+ } else if (flip_or_move) {
+ old_fb = set->crtc->fb;
+ if (set->crtc->fb != set->fb)
+ set->crtc->fb = set->fb;
+ crtc_funcs->mode_set_base(set->crtc, set->x, set->y, old_fb);
+ }
+
+ kfree(save_encoders);
+ kfree(save_crtcs);
+ return 0;
+
+fail_set_mode:
+ set->crtc->enabled = save_enabled;
+ count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ connector->encoder->crtc = save_crtcs[count++];
+fail_no_encoder:
+ kfree(save_crtcs);
+ count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ connector->encoder = save_encoders[count++];
+ }
+ kfree(save_encoders);
+ return ret;
+}
+EXPORT_SYMBOL(drm_crtc_helper_set_config);
+
+bool drm_helper_plugged_event(struct drm_device *dev)
+{
+ DRM_DEBUG("\n");
+
+ drm_helper_probe_connector_modes(dev, dev->mode_config.max_width,
+ dev->mode_config.max_height);
+
+ drm_setup_crtcs(dev);
+
+ /* alert the driver fb layer */
+ dev->mode_config.funcs->fb_changed(dev);
+
+ /* FIXME: send hotplug event */
+ return true;
+}
+/**
+ * drm_initial_config - setup a sane initial connector configuration
+ * @dev: DRM device
+ * @can_grow: this configuration is growable
+ *
+ * LOCKING:
+ * Called at init time, must take mode config lock.
+ *
+ * Scan the CRTCs and connectors and try to put together an initial setup.
+ * At the moment, this is a cloned configuration across all heads with
+ * a new framebuffer object as the backing store.
+ *
+ * RETURNS:
+ * Zero if everything went ok, nonzero otherwise.
+ */
+bool drm_helper_initial_config(struct drm_device *dev, bool can_grow)
+{
+ int ret = false;
+
+ drm_helper_plugged_event(dev);
+ return ret;
+}
+EXPORT_SYMBOL(drm_helper_initial_config);
+
+/**
+ * drm_hotplug_stage_two
+ * @dev DRM device
+ * @connector hotpluged connector
+ *
+ * LOCKING.
+ * Caller must hold mode config lock, function might grab struct lock.
+ *
+ * Stage two of a hotplug.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_helper_hotplug_stage_two(struct drm_device *dev)
+{
+ drm_helper_plugged_event(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_helper_hotplug_stage_two);
+
+int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
+ struct drm_mode_fb_cmd *mode_cmd)
+{
+ fb->width = mode_cmd->width;
+ fb->height = mode_cmd->height;
+ fb->pitch = mode_cmd->pitch;
+ fb->bits_per_pixel = mode_cmd->bpp;
+ fb->depth = mode_cmd->depth;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
+
+int drm_helper_resume_force_mode(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+ int ret;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+
+ if (!crtc->enabled)
+ continue;
+
+ ret = drm_crtc_helper_set_mode(crtc, &crtc->mode,
+ crtc->x, crtc->y, crtc->fb);
+
+ if (ret == false)
+ DRM_ERROR("failed to set mode on crtc %p\n", crtc);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(drm_helper_resume_force_mode);
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 996097acb5e7..febb517ee679 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -74,6 +74,9 @@ static struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_getsareactx, DRM_AUTH),
+ DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY),
+
DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_addctx, DRM_AUTH|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_modctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
@@ -123,6 +126,23 @@ static struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, 0),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW),
};
#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )
@@ -138,8 +158,6 @@ static struct drm_ioctl_desc drm_ioctls[] = {
*/
int drm_lastclose(struct drm_device * dev)
{
- struct drm_magic_entry *pt, *next;
- struct drm_map_list *r_list, *list_t;
struct drm_vma_entry *vma, *vma_temp;
int i;
@@ -149,13 +167,7 @@ int drm_lastclose(struct drm_device * dev)
dev->driver->lastclose(dev);
DRM_DEBUG("driver lastclose completed\n");
- if (dev->unique) {
- drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER);
- dev->unique = NULL;
- dev->unique_len = 0;
- }
-
- if (dev->irq_enabled)
+ if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET))
drm_irq_uninstall(dev);
mutex_lock(&dev->struct_mutex);
@@ -164,18 +176,9 @@ int drm_lastclose(struct drm_device * dev)
drm_drawable_free_all(dev);
del_timer(&dev->timer);
- /* Clear pid list */
- if (dev->magicfree.next) {
- list_for_each_entry_safe(pt, next, &dev->magicfree, head) {
- list_del(&pt->head);
- drm_ht_remove_item(&dev->magiclist, &pt->hash_item);
- drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
- }
- drm_ht_remove(&dev->magiclist);
- }
-
/* Clear AGP information */
- if (drm_core_has_AGP(dev) && dev->agp) {
+ if (drm_core_has_AGP(dev) && dev->agp &&
+ !drm_core_check_feature(dev, DRIVER_MODESET)) {
struct drm_agp_mem *entry, *tempe;
/* Remove AGP resources, but leave dev->agp
@@ -194,7 +197,8 @@ int drm_lastclose(struct drm_device * dev)
dev->agp->acquired = 0;
dev->agp->enabled = 0;
}
- if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg) {
+ if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg &&
+ !drm_core_check_feature(dev, DRIVER_MODESET)) {
drm_sg_cleanup(dev->sg);
dev->sg = NULL;
}
@@ -205,13 +209,6 @@ int drm_lastclose(struct drm_device * dev)
drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
}
- list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
- if (!(r_list->map->flags & _DRM_DRIVER)) {
- drm_rmmap_locked(dev, r_list->map);
- r_list = NULL;
- }
- }
-
if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE) && dev->queuelist) {
for (i = 0; i < dev->queue_count; i++) {
if (dev->queuelist[i]) {
@@ -228,14 +225,11 @@ int drm_lastclose(struct drm_device * dev)
}
dev->queue_count = 0;
- if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
+ if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
+ !drm_core_check_feature(dev, DRIVER_MODESET))
drm_dma_takedown(dev);
- if (dev->lock.hw_lock) {
- dev->sigdata.lock = dev->lock.hw_lock = NULL; /* SHM removed */
- dev->lock.file_priv = NULL;
- wake_up_interruptible(&dev->lock.lock_queue);
- }
+ dev->dev_mapping = NULL;
mutex_unlock(&dev->struct_mutex);
DRM_DEBUG("lastclose completed\n");
@@ -263,6 +257,8 @@ int drm_init(struct drm_driver *driver)
DRM_DEBUG("\n");
+ INIT_LIST_HEAD(&driver->device_list);
+
for (i = 0; driver->pci_driver.id_table[i].vendor != 0; i++) {
pid = (struct pci_device_id *)&driver->pci_driver.id_table[i];
@@ -329,35 +325,24 @@ static void drm_cleanup(struct drm_device * dev)
drm_ht_remove(&dev->map_hash);
drm_ctxbitmap_cleanup(dev);
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_put_minor(&dev->control);
+
+ if (dev->driver->driver_features & DRIVER_GEM)
+ drm_gem_destroy(dev);
+
drm_put_minor(&dev->primary);
if (drm_put_dev(dev))
DRM_ERROR("Cannot unload module\n");
}
-static int drm_minors_cleanup(int id, void *ptr, void *data)
-{
- struct drm_minor *minor = ptr;
- struct drm_device *dev;
- struct drm_driver *driver = data;
-
- dev = minor->dev;
- if (minor->dev->driver != driver)
- return 0;
-
- if (minor->type != DRM_MINOR_LEGACY)
- return 0;
-
- if (dev)
- pci_dev_put(dev->pdev);
- drm_cleanup(dev);
- return 1;
-}
-
void drm_exit(struct drm_driver *driver)
{
+ struct drm_device *dev, *tmp;
DRM_DEBUG("\n");
- idr_for_each(&drm_minors_idr, &drm_minors_cleanup, driver);
+ list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item)
+ drm_cleanup(dev);
DRM_INFO("Module unloaded\n");
}
@@ -503,7 +488,7 @@ int drm_ioctl(struct inode *inode, struct file *filp,
retcode = -EINVAL;
} else if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) ||
((ioctl->flags & DRM_AUTH) && !file_priv->authenticated) ||
- ((ioctl->flags & DRM_MASTER) && !file_priv->master)) {
+ ((ioctl->flags & DRM_MASTER) && !file_priv->is_master)) {
retcode = -EACCES;
} else {
if (cmd & (IOC_IN | IOC_OUT)) {
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
new file mode 100644
index 000000000000..0fbb0da342cb
--- /dev/null
+++ b/drivers/gpu/drm/drm_edid.c
@@ -0,0 +1,732 @@
+/*
+ * Copyright (c) 2006 Luc Verhaegen (quirks list)
+ * Copyright (c) 2007-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from
+ * FB layer.
+ * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * 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 NON-INFRINGEMENT. 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/kernel.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include "drmP.h"
+#include "drm_edid.h"
+
+/*
+ * TODO:
+ * - support EDID 1.4 (incl. CE blocks)
+ */
+
+/*
+ * EDID blocks out in the wild have a variety of bugs, try to collect
+ * them here (note that userspace may work around broken monitors first,
+ * but fixes should make their way here so that the kernel "just works"
+ * on as many displays as possible).
+ */
+
+/* First detailed mode wrong, use largest 60Hz mode */
+#define EDID_QUIRK_PREFER_LARGE_60 (1 << 0)
+/* Reported 135MHz pixel clock is too high, needs adjustment */
+#define EDID_QUIRK_135_CLOCK_TOO_HIGH (1 << 1)
+/* Prefer the largest mode at 75 Hz */
+#define EDID_QUIRK_PREFER_LARGE_75 (1 << 2)
+/* Detail timing is in cm not mm */
+#define EDID_QUIRK_DETAILED_IN_CM (1 << 3)
+/* Detailed timing descriptors have bogus size values, so just take the
+ * maximum size and use that.
+ */
+#define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE (1 << 4)
+/* Monitor forgot to set the first detailed is preferred bit. */
+#define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5)
+/* use +hsync +vsync for detailed mode */
+#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6)
+
+static struct edid_quirk {
+ char *vendor;
+ int product_id;
+ u32 quirks;
+} edid_quirk_list[] = {
+ /* Acer AL1706 */
+ { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 },
+ /* Acer F51 */
+ { "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 },
+ /* Unknown Acer */
+ { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
+
+ /* Belinea 10 15 55 */
+ { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 },
+ { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 },
+
+ /* Envision Peripherals, Inc. EN-7100e */
+ { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH },
+
+ /* Funai Electronics PM36B */
+ { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 |
+ EDID_QUIRK_DETAILED_IN_CM },
+
+ /* LG Philips LCD LP154W01-A5 */
+ { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
+ { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
+
+ /* Philips 107p5 CRT */
+ { "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
+
+ /* Proview AY765C */
+ { "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
+
+ /* Samsung SyncMaster 205BW. Note: irony */
+ { "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP },
+ /* Samsung SyncMaster 22[5-6]BW */
+ { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 },
+ { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 },
+};
+
+
+/* Valid EDID header has these bytes */
+static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
+
+/**
+ * edid_is_valid - sanity check EDID data
+ * @edid: EDID data
+ *
+ * Sanity check the EDID block by looking at the header, the version number
+ * and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's
+ * valid.
+ */
+static bool edid_is_valid(struct edid *edid)
+{
+ int i;
+ u8 csum = 0;
+ u8 *raw_edid = (u8 *)edid;
+
+ if (memcmp(edid->header, edid_header, sizeof(edid_header)))
+ goto bad;
+ if (edid->version != 1) {
+ DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version);
+ goto bad;
+ }
+ if (edid->revision <= 0 || edid->revision > 3) {
+ DRM_ERROR("EDID has minor version %d, which is not between 0-3\n", edid->revision);
+ goto bad;
+ }
+
+ for (i = 0; i < EDID_LENGTH; i++)
+ csum += raw_edid[i];
+ if (csum) {
+ DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum);
+ goto bad;
+ }
+
+ return 1;
+
+bad:
+ if (raw_edid) {
+ DRM_ERROR("Raw EDID:\n");
+ print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH);
+ printk("\n");
+ }
+ return 0;
+}
+
+/**
+ * edid_vendor - match a string against EDID's obfuscated vendor field
+ * @edid: EDID to match
+ * @vendor: vendor string
+ *
+ * Returns true if @vendor is in @edid, false otherwise
+ */
+static bool edid_vendor(struct edid *edid, char *vendor)
+{
+ char edid_vendor[3];
+
+ edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@';
+ edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) |
+ ((edid->mfg_id[1] & 0xe0) >> 5)) + '@';
+ edid_vendor[2] = (edid->mfg_id[2] & 0x1f) + '@';
+
+ return !strncmp(edid_vendor, vendor, 3);
+}
+
+/**
+ * edid_get_quirks - return quirk flags for a given EDID
+ * @edid: EDID to process
+ *
+ * This tells subsequent routines what fixes they need to apply.
+ */
+static u32 edid_get_quirks(struct edid *edid)
+{
+ struct edid_quirk *quirk;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) {
+ quirk = &edid_quirk_list[i];
+
+ if (edid_vendor(edid, quirk->vendor) &&
+ (EDID_PRODUCT_ID(edid) == quirk->product_id))
+ return quirk->quirks;
+ }
+
+ return 0;
+}
+
+#define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
+#define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh))
+
+
+/**
+ * edid_fixup_preferred - set preferred modes based on quirk list
+ * @connector: has mode list to fix up
+ * @quirks: quirks list
+ *
+ * Walk the mode list for @connector, clearing the preferred status
+ * on existing modes and setting it anew for the right mode ala @quirks.
+ */
+static void edid_fixup_preferred(struct drm_connector *connector,
+ u32 quirks)
+{
+ struct drm_display_mode *t, *cur_mode, *preferred_mode;
+ int target_refresh = 0;
+
+ if (list_empty(&connector->probed_modes))
+ return;
+
+ if (quirks & EDID_QUIRK_PREFER_LARGE_60)
+ target_refresh = 60;
+ if (quirks & EDID_QUIRK_PREFER_LARGE_75)
+ target_refresh = 75;
+
+ preferred_mode = list_first_entry(&connector->probed_modes,
+ struct drm_display_mode, head);
+
+ list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) {
+ cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED;
+
+ if (cur_mode == preferred_mode)
+ continue;
+
+ /* Largest mode is preferred */
+ if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode))
+ preferred_mode = cur_mode;
+
+ /* At a given size, try to get closest to target refresh */
+ if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) &&
+ MODE_REFRESH_DIFF(cur_mode, target_refresh) <
+ MODE_REFRESH_DIFF(preferred_mode, target_refresh)) {
+ preferred_mode = cur_mode;
+ }
+ }
+
+ preferred_mode->type |= DRM_MODE_TYPE_PREFERRED;
+}
+
+/**
+ * drm_mode_std - convert standard mode info (width, height, refresh) into mode
+ * @t: standard timing params
+ *
+ * Take the standard timing params (in this case width, aspect, and refresh)
+ * and convert them into a real mode using CVT.
+ *
+ * Punts for now, but should eventually use the FB layer's CVT based mode
+ * generation code.
+ */
+struct drm_display_mode *drm_mode_std(struct drm_device *dev,
+ struct std_timing *t)
+{
+ struct drm_display_mode *mode;
+ int hsize = t->hsize * 8 + 248, vsize;
+
+ mode = drm_mode_create(dev);
+ if (!mode)
+ return NULL;
+
+ if (t->aspect_ratio == 0)
+ vsize = (hsize * 10) / 16;
+ else if (t->aspect_ratio == 1)
+ vsize = (hsize * 3) / 4;
+ else if (t->aspect_ratio == 2)
+ vsize = (hsize * 4) / 5;
+ else
+ vsize = (hsize * 9) / 16;
+
+ drm_mode_set_name(mode);
+
+ return mode;
+}
+
+/**
+ * drm_mode_detailed - create a new mode from an EDID detailed timing section
+ * @dev: DRM device (needed to create new mode)
+ * @edid: EDID block
+ * @timing: EDID detailed timing info
+ * @quirks: quirks to apply
+ *
+ * An EDID detailed timing block contains enough info for us to create and
+ * return a new struct drm_display_mode.
+ */
+static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
+ struct edid *edid,
+ struct detailed_timing *timing,
+ u32 quirks)
+{
+ struct drm_display_mode *mode;
+ struct detailed_pixel_timing *pt = &timing->data.pixel_data;
+
+ if (pt->stereo) {
+ printk(KERN_WARNING "stereo mode not supported\n");
+ return NULL;
+ }
+ if (!pt->separate_sync) {
+ printk(KERN_WARNING "integrated sync not supported\n");
+ return NULL;
+ }
+
+ mode = drm_mode_create(dev);
+ if (!mode)
+ return NULL;
+
+ mode->type = DRM_MODE_TYPE_DRIVER;
+
+ if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH)
+ timing->pixel_clock = 1088;
+
+ mode->clock = timing->pixel_clock * 10;
+
+ mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo;
+ mode->hsync_start = mode->hdisplay + ((pt->hsync_offset_hi << 8) |
+ pt->hsync_offset_lo);
+ mode->hsync_end = mode->hsync_start +
+ ((pt->hsync_pulse_width_hi << 8) |
+ pt->hsync_pulse_width_lo);
+ mode->htotal = mode->hdisplay + ((pt->hblank_hi << 8) | pt->hblank_lo);
+
+ mode->vdisplay = (pt->vactive_hi << 8) | pt->vactive_lo;
+ mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 8) |
+ pt->vsync_offset_lo);
+ mode->vsync_end = mode->vsync_start +
+ ((pt->vsync_pulse_width_hi << 8) |
+ pt->vsync_pulse_width_lo);
+ mode->vtotal = mode->vdisplay + ((pt->vblank_hi << 8) | pt->vblank_lo);
+
+ drm_mode_set_name(mode);
+
+ if (pt->interlaced)
+ mode->flags |= DRM_MODE_FLAG_INTERLACE;
+
+ if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
+ pt->hsync_positive = 1;
+ pt->vsync_positive = 1;
+ }
+
+ mode->flags |= pt->hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
+ mode->flags |= pt->vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
+
+ mode->width_mm = pt->width_mm_lo | (pt->width_mm_hi << 8);
+ mode->height_mm = pt->height_mm_lo | (pt->height_mm_hi << 8);
+
+ if (quirks & EDID_QUIRK_DETAILED_IN_CM) {
+ mode->width_mm *= 10;
+ mode->height_mm *= 10;
+ }
+
+ if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
+ mode->width_mm = edid->width_cm * 10;
+ mode->height_mm = edid->height_cm * 10;
+ }
+
+ return mode;
+}
+
+/*
+ * Detailed mode info for the EDID "established modes" data to use.
+ */
+static struct drm_display_mode edid_est_modes[] = {
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
+ 968, 1056, 0, 600, 601, 605, 628, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
+ 896, 1024, 0, 600, 601, 603, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
+ 720, 840, 0, 480, 481, 484, 500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
+ 704, 832, 0, 480, 489, 491, 520, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
+ 768, 864, 0, 480, 483, 486, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656,
+ 752, 800, 0, 480, 490, 492, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
+ 846, 900, 0, 400, 421, 423, 449, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
+ 846, 900, 0, 400, 412, 414, 449, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040,
+ 1136, 1312, 0, 768, 769, 772, 800, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
+ 1184, 1328, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
+ 1184, 1344, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
+ 1208, 1264, 0, 768, 768, 776, 817, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */
+ { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
+ 928, 1152, 0, 624, 625, 628, 667, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
+ 896, 1056, 0, 600, 601, 604, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
+ 976, 1040, 0, 600, 637, 643, 666, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */
+ { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
+ 1344, 1600, 0, 864, 865, 868, 900, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */
+};
+
+#define EDID_EST_TIMINGS 16
+#define EDID_STD_TIMINGS 8
+#define EDID_DETAILED_TIMINGS 4
+
+/**
+ * add_established_modes - get est. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Each EDID block contains a bitmap of the supported "established modes" list
+ * (defined above). Tease them out and add them to the global modes list.
+ */
+static int add_established_modes(struct drm_connector *connector, struct edid *edid)
+{
+ struct drm_device *dev = connector->dev;
+ unsigned long est_bits = edid->established_timings.t1 |
+ (edid->established_timings.t2 << 8) |
+ ((edid->established_timings.mfg_rsvd & 0x80) << 9);
+ int i, modes = 0;
+
+ for (i = 0; i <= EDID_EST_TIMINGS; i++)
+ if (est_bits & (1<<i)) {
+ struct drm_display_mode *newmode;
+ newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+
+ return modes;
+}
+
+/**
+ * add_standard_modes - get std. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Standard modes can be calculated using the CVT standard. Grab them from
+ * @edid, calculate them, and add them to the list.
+ */
+static int add_standard_modes(struct drm_connector *connector, struct edid *edid)
+{
+ struct drm_device *dev = connector->dev;
+ int i, modes = 0;
+
+ for (i = 0; i < EDID_STD_TIMINGS; i++) {
+ struct std_timing *t = &edid->standard_timings[i];
+ struct drm_display_mode *newmode;
+
+ /* If std timings bytes are 1, 1 it's empty */
+ if (t->hsize == 1 && (t->aspect_ratio | t->vfreq) == 1)
+ continue;
+
+ newmode = drm_mode_std(dev, &edid->standard_timings[i]);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+
+ return modes;
+}
+
+/**
+ * add_detailed_modes - get detailed mode info from EDID data
+ * @connector: attached connector
+ * @edid: EDID block to scan
+ * @quirks: quirks to apply
+ *
+ * Some of the detailed timing sections may contain mode information. Grab
+ * it and add it to the list.
+ */
+static int add_detailed_info(struct drm_connector *connector,
+ struct edid *edid, u32 quirks)
+{
+ struct drm_device *dev = connector->dev;
+ int i, j, modes = 0;
+
+ for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
+ struct detailed_timing *timing = &edid->detailed_timings[i];
+ struct detailed_non_pixel *data = &timing->data.other_data;
+ struct drm_display_mode *newmode;
+
+ /* EDID up to and including 1.2 may put monitor info here */
+ if (edid->version == 1 && edid->revision < 3)
+ continue;
+
+ /* Detailed mode timing */
+ if (timing->pixel_clock) {
+ newmode = drm_mode_detailed(dev, edid, timing, quirks);
+ if (!newmode)
+ continue;
+
+ /* First detailed mode is preferred */
+ if (i == 0 && edid->preferred_timing)
+ newmode->type |= DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, newmode);
+
+ modes++;
+ continue;
+ }
+
+ /* Other timing or info */
+ switch (data->type) {
+ case EDID_DETAIL_MONITOR_SERIAL:
+ break;
+ case EDID_DETAIL_MONITOR_STRING:
+ break;
+ case EDID_DETAIL_MONITOR_RANGE:
+ /* Get monitor range data */
+ break;
+ case EDID_DETAIL_MONITOR_NAME:
+ break;
+ case EDID_DETAIL_MONITOR_CPDATA:
+ break;
+ case EDID_DETAIL_STD_MODES:
+ /* Five modes per detailed section */
+ for (j = 0; j < 5; i++) {
+ struct std_timing *std;
+ struct drm_display_mode *newmode;
+
+ std = &data->data.timings[j];
+ newmode = drm_mode_std(dev, std);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return modes;
+}
+
+#define DDC_ADDR 0x50
+
+unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter)
+{
+ unsigned char start = 0x0;
+ unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ struct i2c_msg msgs[] = {
+ {
+ .addr = DDC_ADDR,
+ .flags = 0,
+ .len = 1,
+ .buf = &start,
+ }, {
+ .addr = DDC_ADDR,
+ .flags = I2C_M_RD,
+ .len = EDID_LENGTH,
+ .buf = buf,
+ }
+ };
+
+ if (!buf) {
+ dev_warn(&adapter->dev, "unable to allocate memory for EDID "
+ "block.\n");
+ return NULL;
+ }
+
+ if (i2c_transfer(adapter, msgs, 2) == 2)
+ return buf;
+
+ dev_info(&adapter->dev, "unable to read EDID block.\n");
+ kfree(buf);
+ return NULL;
+}
+EXPORT_SYMBOL(drm_do_probe_ddc_edid);
+
+static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
+{
+ struct i2c_algo_bit_data *algo_data = adapter->algo_data;
+ unsigned char *edid = NULL;
+ int i, j;
+
+ algo_data->setscl(algo_data->data, 1);
+
+ for (i = 0; i < 1; i++) {
+ /* For some old monitors we need the
+ * following process to initialize/stop DDC
+ */
+ algo_data->setsda(algo_data->data, 1);
+ msleep(13);
+
+ algo_data->setscl(algo_data->data, 1);
+ for (j = 0; j < 5; j++) {
+ msleep(10);
+ if (algo_data->getscl(algo_data->data))
+ break;
+ }
+ if (j == 5)
+ continue;
+
+ algo_data->setsda(algo_data->data, 0);
+ msleep(15);
+ algo_data->setscl(algo_data->data, 0);
+ msleep(15);
+ algo_data->setsda(algo_data->data, 1);
+ msleep(15);
+
+ /* Do the real work */
+ edid = drm_do_probe_ddc_edid(adapter);
+ algo_data->setsda(algo_data->data, 0);
+ algo_data->setscl(algo_data->data, 0);
+ msleep(15);
+
+ algo_data->setscl(algo_data->data, 1);
+ for (j = 0; j < 10; j++) {
+ msleep(10);
+ if (algo_data->getscl(algo_data->data))
+ break;
+ }
+
+ algo_data->setsda(algo_data->data, 1);
+ msleep(15);
+ algo_data->setscl(algo_data->data, 0);
+ algo_data->setsda(algo_data->data, 0);
+ if (edid)
+ break;
+ }
+ /* Release the DDC lines when done or the Apple Cinema HD display
+ * will switch off
+ */
+ algo_data->setsda(algo_data->data, 1);
+ algo_data->setscl(algo_data->data, 1);
+
+ return edid;
+}
+
+/**
+ * drm_get_edid - get EDID data, if available
+ * @connector: connector we're probing
+ * @adapter: i2c adapter to use for DDC
+ *
+ * Poke the given connector's i2c channel to grab EDID data if possible.
+ *
+ * Return edid data or NULL if we couldn't find any.
+ */
+struct edid *drm_get_edid(struct drm_connector *connector,
+ struct i2c_adapter *adapter)
+{
+ struct edid *edid;
+
+ edid = (struct edid *)drm_ddc_read(adapter);
+ if (!edid) {
+ dev_warn(&connector->dev->pdev->dev, "%s: no EDID data\n",
+ drm_get_connector_name(connector));
+ return NULL;
+ }
+ if (!edid_is_valid(edid)) {
+ dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
+ drm_get_connector_name(connector));
+ kfree(edid);
+ return NULL;
+ }
+
+ connector->display_info.raw_edid = (char *)edid;
+
+ return edid;
+}
+EXPORT_SYMBOL(drm_get_edid);
+
+/**
+ * drm_add_edid_modes - add modes from EDID data, if available
+ * @connector: connector we're probing
+ * @edid: edid data
+ *
+ * Add the specified modes to the connector's mode list.
+ *
+ * Return number of modes added or 0 if we couldn't find any.
+ */
+int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
+{
+ int num_modes = 0;
+ u32 quirks;
+
+ if (edid == NULL) {
+ return 0;
+ }
+ if (!edid_is_valid(edid)) {
+ dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
+ drm_get_connector_name(connector));
+ return 0;
+ }
+
+ quirks = edid_get_quirks(edid);
+
+ num_modes += add_established_modes(connector, edid);
+ num_modes += add_standard_modes(connector, edid);
+ num_modes += add_detailed_info(connector, edid, quirks);
+
+ if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
+ edid_fixup_preferred(connector, quirks);
+
+ connector->display_info.serration_vsync = edid->serration_vsync;
+ connector->display_info.sync_on_green = edid->sync_on_green;
+ connector->display_info.composite_sync = edid->composite_sync;
+ connector->display_info.separate_syncs = edid->separate_syncs;
+ connector->display_info.blank_to_black = edid->blank_to_black;
+ connector->display_info.video_level = edid->video_level;
+ connector->display_info.digital = edid->digital;
+ connector->display_info.width_mm = edid->width_cm * 10;
+ connector->display_info.height_mm = edid->height_cm * 10;
+ connector->display_info.gamma = edid->gamma;
+ connector->display_info.gtf_supported = edid->default_gtf;
+ connector->display_info.standard_color = edid->standard_color;
+ connector->display_info.display_type = edid->display_type;
+ connector->display_info.active_off_supported = edid->pm_active_off;
+ connector->display_info.suspend_supported = edid->pm_suspend;
+ connector->display_info.standby_supported = edid->pm_standby;
+ connector->display_info.gamma = edid->gamma;
+
+ return num_modes;
+}
+EXPORT_SYMBOL(drm_add_edid_modes);
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 78eeed5caaff..3733e36d135e 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -35,7 +35,6 @@
*/
#include "drmP.h"
-#include "drm_sarea.h"
#include <linux/poll.h>
#include <linux/smp_lock.h>
@@ -44,10 +43,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
static int drm_setup(struct drm_device * dev)
{
- drm_local_map_t *map;
int i;
int ret;
- u32 sareapage;
if (dev->driver->firstopen) {
ret = dev->driver->firstopen(dev);
@@ -55,20 +52,14 @@ static int drm_setup(struct drm_device * dev)
return ret;
}
- dev->magicfree.next = NULL;
-
- /* prebuild the SAREA */
- sareapage = max_t(unsigned, SAREA_MAX, PAGE_SIZE);
- i = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK, &map);
- if (i != 0)
- return i;
-
atomic_set(&dev->ioctl_count, 0);
atomic_set(&dev->vma_count, 0);
- dev->buf_use = 0;
- atomic_set(&dev->buf_alloc, 0);
- if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) {
+ if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
+ !drm_core_check_feature(dev, DRIVER_MODESET)) {
+ dev->buf_use = 0;
+ atomic_set(&dev->buf_alloc, 0);
+
i = drm_dma_setup(dev);
if (i < 0)
return i;
@@ -77,16 +68,12 @@ static int drm_setup(struct drm_device * dev)
for (i = 0; i < ARRAY_SIZE(dev->counts); i++)
atomic_set(&dev->counts[i], 0);
- drm_ht_create(&dev->magiclist, DRM_MAGIC_HASH_ORDER);
- INIT_LIST_HEAD(&dev->magicfree);
-
dev->sigdata.lock = NULL;
- init_waitqueue_head(&dev->lock.lock_queue);
+
dev->queue_count = 0;
dev->queue_reserved = 0;
dev->queue_slots = 0;
dev->queuelist = NULL;
- dev->irq_enabled = 0;
dev->context_flag = 0;
dev->interrupt_flag = 0;
dev->dma_flag = 0;
@@ -147,10 +134,20 @@ int drm_open(struct inode *inode, struct file *filp)
spin_lock(&dev->count_lock);
if (!dev->open_count++) {
spin_unlock(&dev->count_lock);
- return drm_setup(dev);
+ retcode = drm_setup(dev);
+ goto out;
}
spin_unlock(&dev->count_lock);
}
+out:
+ mutex_lock(&dev->struct_mutex);
+ if (minor->type == DRM_MINOR_LEGACY) {
+ BUG_ON((dev->dev_mapping != NULL) &&
+ (dev->dev_mapping != inode->i_mapping));
+ if (dev->dev_mapping == NULL)
+ dev->dev_mapping = inode->i_mapping;
+ }
+ mutex_unlock(&dev->struct_mutex);
return retcode;
}
@@ -255,6 +252,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
priv->lock_count = 0;
INIT_LIST_HEAD(&priv->lhead);
+ INIT_LIST_HEAD(&priv->fbs);
if (dev->driver->driver_features & DRIVER_GEM)
drm_gem_open(dev, priv);
@@ -265,10 +263,42 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
goto out_free;
}
+
+ /* if there is no current master make this fd it */
mutex_lock(&dev->struct_mutex);
- if (list_empty(&dev->filelist))
- priv->master = 1;
+ if (!priv->minor->master) {
+ /* create a new master */
+ priv->minor->master = drm_master_create(priv->minor);
+ if (!priv->minor->master) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ priv->is_master = 1;
+ /* take another reference for the copy in the local file priv */
+ priv->master = drm_master_get(priv->minor->master);
+
+ priv->authenticated = 1;
+
+ mutex_unlock(&dev->struct_mutex);
+ if (dev->driver->master_create) {
+ ret = dev->driver->master_create(dev, priv->master);
+ if (ret) {
+ mutex_lock(&dev->struct_mutex);
+ /* drop both references if this fails */
+ drm_master_put(&priv->minor->master);
+ drm_master_put(&priv->master);
+ mutex_unlock(&dev->struct_mutex);
+ goto out_free;
+ }
+ }
+ } else {
+ /* get a reference to the master */
+ priv->master = drm_master_get(priv->minor->master);
+ mutex_unlock(&dev->struct_mutex);
+ }
+
+ mutex_lock(&dev->struct_mutex);
list_add(&priv->lhead, &dev->filelist);
mutex_unlock(&dev->struct_mutex);
@@ -314,6 +344,74 @@ int drm_fasync(int fd, struct file *filp, int on)
}
EXPORT_SYMBOL(drm_fasync);
+/*
+ * Reclaim locked buffers; note that this may be a bad idea if the current
+ * context doesn't have the hw lock...
+ */
+static void drm_reclaim_locked_buffers(struct drm_device *dev, struct file *f)
+{
+ struct drm_file *file_priv = f->private_data;
+
+ if (drm_i_have_hw_lock(dev, file_priv)) {
+ dev->driver->reclaim_buffers_locked(dev, file_priv);
+ } else {
+ unsigned long _end = jiffies + 3 * DRM_HZ;
+ int locked = 0;
+
+ drm_idlelock_take(&file_priv->master->lock);
+
+ /*
+ * Wait for a while.
+ */
+ do {
+ spin_lock_bh(&file_priv->master->lock.spinlock);
+ locked = file_priv->master->lock.idle_has_lock;
+ spin_unlock_bh(&file_priv->master->lock.spinlock);
+ if (locked)
+ break;
+ schedule();
+ } while (!time_after_eq(jiffies, _end));
+
+ if (!locked) {
+ DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n"
+ "\tdriver to use reclaim_buffers_idlelocked() instead.\n"
+ "\tI will go on reclaiming the buffers anyway.\n");
+ }
+
+ dev->driver->reclaim_buffers_locked(dev, file_priv);
+ drm_idlelock_release(&file_priv->master->lock);
+ }
+}
+
+static void drm_master_release(struct drm_device *dev, struct file *filp)
+{
+ struct drm_file *file_priv = filp->private_data;
+
+ if (dev->driver->reclaim_buffers_locked &&
+ file_priv->master->lock.hw_lock)
+ drm_reclaim_locked_buffers(dev, filp);
+
+ if (dev->driver->reclaim_buffers_idlelocked &&
+ file_priv->master->lock.hw_lock) {
+ drm_idlelock_take(&file_priv->master->lock);
+ dev->driver->reclaim_buffers_idlelocked(dev, file_priv);
+ drm_idlelock_release(&file_priv->master->lock);
+ }
+
+
+ if (drm_i_have_hw_lock(dev, file_priv)) {
+ DRM_DEBUG("File %p released, freeing lock for context %d\n",
+ filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
+ drm_lock_free(&file_priv->master->lock,
+ _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
+ }
+
+ if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
+ !dev->driver->reclaim_buffers_locked) {
+ dev->driver->reclaim_buffers(dev, file_priv);
+ }
+}
+
/**
* Release file.
*
@@ -348,60 +446,9 @@ int drm_release(struct inode *inode, struct file *filp)
(long)old_encode_dev(file_priv->minor->device),
dev->open_count);
- if (dev->driver->reclaim_buffers_locked && dev->lock.hw_lock) {
- if (drm_i_have_hw_lock(dev, file_priv)) {
- dev->driver->reclaim_buffers_locked(dev, file_priv);
- } else {
- unsigned long endtime = jiffies + 3 * DRM_HZ;
- int locked = 0;
-
- drm_idlelock_take(&dev->lock);
-
- /*
- * Wait for a while.
- */
-
- do{
- spin_lock_bh(&dev->lock.spinlock);
- locked = dev->lock.idle_has_lock;
- spin_unlock_bh(&dev->lock.spinlock);
- if (locked)
- break;
- schedule();
- } while (!time_after_eq(jiffies, endtime));
-
- if (!locked) {
- DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n"
- "\tdriver to use reclaim_buffers_idlelocked() instead.\n"
- "\tI will go on reclaiming the buffers anyway.\n");
- }
-
- dev->driver->reclaim_buffers_locked(dev, file_priv);
- drm_idlelock_release(&dev->lock);
- }
- }
-
- if (dev->driver->reclaim_buffers_idlelocked && dev->lock.hw_lock) {
-
- drm_idlelock_take(&dev->lock);
- dev->driver->reclaim_buffers_idlelocked(dev, file_priv);
- drm_idlelock_release(&dev->lock);
-
- }
-
- if (drm_i_have_hw_lock(dev, file_priv)) {
- DRM_DEBUG("File %p released, freeing lock for context %d\n",
- filp, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
-
- drm_lock_free(&dev->lock,
- _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
- }
-
-
- if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
- !dev->driver->reclaim_buffers_locked) {
- dev->driver->reclaim_buffers(dev, file_priv);
- }
+ /* if the master has gone away we can't do anything with the lock */
+ if (file_priv->minor->master)
+ drm_master_release(dev, filp);
if (dev->driver->driver_features & DRIVER_GEM)
drm_gem_release(dev, file_priv);
@@ -428,12 +475,24 @@ int drm_release(struct inode *inode, struct file *filp)
mutex_unlock(&dev->ctxlist_mutex);
mutex_lock(&dev->struct_mutex);
- if (file_priv->remove_auth_on_close == 1) {
+
+ if (file_priv->is_master) {
struct drm_file *temp;
+ list_for_each_entry(temp, &dev->filelist, lhead) {
+ if ((temp->master == file_priv->master) &&
+ (temp != file_priv))
+ temp->authenticated = 0;
+ }
- list_for_each_entry(temp, &dev->filelist, lhead)
- temp->authenticated = 0;
+ if (file_priv->minor->master == file_priv->master) {
+ /* drop the reference held my the minor */
+ drm_master_put(&file_priv->minor->master);
+ }
}
+
+ /* drop the reference held my the file priv */
+ drm_master_put(&file_priv->master);
+ file_priv->is_master = 0;
list_del(&file_priv->lhead);
mutex_unlock(&dev->struct_mutex);
@@ -448,9 +507,9 @@ int drm_release(struct inode *inode, struct file *filp)
atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
spin_lock(&dev->count_lock);
if (!--dev->open_count) {
- if (atomic_read(&dev->ioctl_count) || dev->blocked) {
- DRM_ERROR("Device busy: %d %d\n",
- atomic_read(&dev->ioctl_count), dev->blocked);
+ if (atomic_read(&dev->ioctl_count)) {
+ DRM_ERROR("Device busy: %d\n",
+ atomic_read(&dev->ioctl_count));
spin_unlock(&dev->count_lock);
unlock_kernel();
return -EBUSY;
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index ccd1afdede02..9da581452874 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -64,6 +64,13 @@
* up at a later date, and as our interface with shmfs for memory allocation.
*/
+/*
+ * We make up offsets for buffer objects so we can recognize them at
+ * mmap time.
+ */
+#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1)
+#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16)
+
/**
* Initialize the GEM device fields
*/
@@ -71,6 +78,8 @@
int
drm_gem_init(struct drm_device *dev)
{
+ struct drm_gem_mm *mm;
+
spin_lock_init(&dev->object_name_lock);
idr_init(&dev->object_name_idr);
atomic_set(&dev->object_count, 0);
@@ -79,9 +88,41 @@ drm_gem_init(struct drm_device *dev)
atomic_set(&dev->pin_memory, 0);
atomic_set(&dev->gtt_count, 0);
atomic_set(&dev->gtt_memory, 0);
+
+ mm = drm_calloc(1, sizeof(struct drm_gem_mm), DRM_MEM_MM);
+ if (!mm) {
+ DRM_ERROR("out of memory\n");
+ return -ENOMEM;
+ }
+
+ dev->mm_private = mm;
+
+ if (drm_ht_create(&mm->offset_hash, 19)) {
+ drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM);
+ return -ENOMEM;
+ }
+
+ if (drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START,
+ DRM_FILE_PAGE_OFFSET_SIZE)) {
+ drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM);
+ drm_ht_remove(&mm->offset_hash);
+ return -ENOMEM;
+ }
+
return 0;
}
+void
+drm_gem_destroy(struct drm_device *dev)
+{
+ struct drm_gem_mm *mm = dev->mm_private;
+
+ drm_mm_takedown(&mm->offset_manager);
+ drm_ht_remove(&mm->offset_hash);
+ drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM);
+ dev->mm_private = NULL;
+}
+
/**
* Allocate a GEM object of the specified size with shmfs backing store
*/
@@ -419,3 +460,73 @@ drm_gem_object_handle_free(struct kref *kref)
}
EXPORT_SYMBOL(drm_gem_object_handle_free);
+/**
+ * drm_gem_mmap - memory map routine for GEM objects
+ * @filp: DRM file pointer
+ * @vma: VMA for the area to be mapped
+ *
+ * If a driver supports GEM object mapping, mmap calls on the DRM file
+ * descriptor will end up here.
+ *
+ * If we find the object based on the offset passed in (vma->vm_pgoff will
+ * contain the fake offset we created when the GTT map ioctl was called on
+ * the object), we set up the driver fault handler so that any accesses
+ * to the object can be trapped, to perform migration, GTT binding, surface
+ * register allocation, or performance monitoring.
+ */
+int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->minor->dev;
+ struct drm_gem_mm *mm = dev->mm_private;
+ struct drm_map *map = NULL;
+ struct drm_gem_object *obj;
+ struct drm_hash_item *hash;
+ unsigned long prot;
+ int ret = 0;
+
+ mutex_lock(&dev->struct_mutex);
+
+ if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
+ mutex_unlock(&dev->struct_mutex);
+ return drm_mmap(filp, vma);
+ }
+
+ map = drm_hash_entry(hash, struct drm_map_list, hash)->map;
+ if (!map ||
+ ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) {
+ ret = -EPERM;
+ goto out_unlock;
+ }
+
+ /* Check for valid size. */
+ if (map->size < vma->vm_end - vma->vm_start) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ obj = map->handle;
+ if (!obj->dev->driver->gem_vm_ops) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ vma->vm_flags |= VM_RESERVED | VM_IO | VM_PFNMAP | VM_DONTEXPAND;
+ vma->vm_ops = obj->dev->driver->gem_vm_ops;
+ vma->vm_private_data = map->handle;
+ /* FIXME: use pgprot_writecombine when available */
+ prot = pgprot_val(vma->vm_page_prot);
+#ifdef CONFIG_X86
+ prot |= _PAGE_CACHE_WC;
+#endif
+ vma->vm_page_prot = __pgprot(prot);
+
+ vma->vm_file = filp; /* Needed for drm_vm_open() */
+ drm_vm_open_locked(vma);
+
+out_unlock:
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_gem_mmap);
diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c
index 33160673a7b7..af539f7d87dd 100644
--- a/drivers/gpu/drm/drm_hashtab.c
+++ b/drivers/gpu/drm/drm_hashtab.c
@@ -127,6 +127,7 @@ int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item)
}
return 0;
}
+EXPORT_SYMBOL(drm_ht_insert_item);
/*
* Just insert an item and return any "bits" bit key that hasn't been
@@ -188,6 +189,7 @@ int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item)
ht->fill--;
return 0;
}
+EXPORT_SYMBOL(drm_ht_remove_item);
void drm_ht_remove(struct drm_open_hash *ht)
{
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 16829fb3089d..1fad76289e66 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -53,12 +53,13 @@ int drm_getunique(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_unique *u = data;
+ struct drm_master *master = file_priv->master;
- if (u->unique_len >= dev->unique_len) {
- if (copy_to_user(u->unique, dev->unique, dev->unique_len))
+ if (u->unique_len >= master->unique_len) {
+ if (copy_to_user(u->unique, master->unique, master->unique_len))
return -EFAULT;
}
- u->unique_len = dev->unique_len;
+ u->unique_len = master->unique_len;
return 0;
}
@@ -81,36 +82,38 @@ int drm_setunique(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_unique *u = data;
+ struct drm_master *master = file_priv->master;
int domain, bus, slot, func, ret;
- if (dev->unique_len || dev->unique)
+ if (master->unique_len || master->unique)
return -EBUSY;
if (!u->unique_len || u->unique_len > 1024)
return -EINVAL;
- dev->unique_len = u->unique_len;
- dev->unique = drm_alloc(u->unique_len + 1, DRM_MEM_DRIVER);
- if (!dev->unique)
+ master->unique_len = u->unique_len;
+ master->unique_size = u->unique_len + 1;
+ master->unique = drm_alloc(master->unique_size, DRM_MEM_DRIVER);
+ if (!master->unique)
return -ENOMEM;
- if (copy_from_user(dev->unique, u->unique, dev->unique_len))
+ if (copy_from_user(master->unique, u->unique, master->unique_len))
return -EFAULT;
- dev->unique[dev->unique_len] = '\0';
+ master->unique[master->unique_len] = '\0';
dev->devname =
drm_alloc(strlen(dev->driver->pci_driver.name) +
- strlen(dev->unique) + 2, DRM_MEM_DRIVER);
+ strlen(master->unique) + 2, DRM_MEM_DRIVER);
if (!dev->devname)
return -ENOMEM;
sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
- dev->unique);
+ master->unique);
/* Return error if the busid submitted doesn't match the device's actual
* busid.
*/
- ret = sscanf(dev->unique, "PCI:%d:%d:%d", &bus, &slot, &func);
+ ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func);
if (ret != 3)
return -EINVAL;
domain = bus >> 8;
@@ -125,34 +128,38 @@ int drm_setunique(struct drm_device *dev, void *data,
return 0;
}
-static int drm_set_busid(struct drm_device * dev)
+static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
{
+ struct drm_master *master = file_priv->master;
int len;
- if (dev->unique != NULL)
- return 0;
+ if (master->unique != NULL)
+ return -EBUSY;
- dev->unique_len = 40;
- dev->unique = drm_alloc(dev->unique_len + 1, DRM_MEM_DRIVER);
- if (dev->unique == NULL)
+ master->unique_len = 40;
+ master->unique_size = master->unique_len;
+ master->unique = drm_alloc(master->unique_size, DRM_MEM_DRIVER);
+ if (master->unique == NULL)
return -ENOMEM;
- len = snprintf(dev->unique, dev->unique_len, "pci:%04x:%02x:%02x.%d",
- drm_get_pci_domain(dev), dev->pdev->bus->number,
+ len = snprintf(master->unique, master->unique_len, "pci:%04x:%02x:%02x.%d",
+ drm_get_pci_domain(dev),
+ dev->pdev->bus->number,
PCI_SLOT(dev->pdev->devfn),
PCI_FUNC(dev->pdev->devfn));
-
- if (len > dev->unique_len)
- DRM_ERROR("Unique buffer overflowed\n");
+ if (len >= master->unique_len)
+ DRM_ERROR("buffer overflow");
+ else
+ master->unique_len = len;
dev->devname =
- drm_alloc(strlen(dev->driver->pci_driver.name) + dev->unique_len +
+ drm_alloc(strlen(dev->driver->pci_driver.name) + master->unique_len +
2, DRM_MEM_DRIVER);
if (dev->devname == NULL)
return -ENOMEM;
sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
- dev->unique);
+ master->unique);
return 0;
}
@@ -276,7 +283,7 @@ int drm_getstats(struct drm_device *dev, void *data,
for (i = 0; i < dev->counters; i++) {
if (dev->types[i] == _DRM_STAT_LOCK)
stats->data[i].value =
- (dev->lock.hw_lock ? dev->lock.hw_lock->lock : 0);
+ (file_priv->master->lock.hw_lock ? file_priv->master->lock.hw_lock->lock : 0);
else
stats->data[i].value = atomic_read(&dev->counts[i]);
stats->data[i].type = dev->types[i];
@@ -318,7 +325,7 @@ int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_pri
/*
* Version 1.1 includes tying of DRM to specific device
*/
- drm_set_busid(dev);
+ drm_set_busid(dev, file_priv);
}
}
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 1e787f894b3c..724e505873cf 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -116,6 +116,9 @@ void drm_vblank_cleanup(struct drm_device *dev)
dev->num_crtcs, DRM_MEM_DRIVER);
drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs,
DRM_MEM_DRIVER);
+ drm_free(dev->last_vblank_wait,
+ sizeof(*dev->last_vblank_wait) * dev->num_crtcs,
+ DRM_MEM_DRIVER);
drm_free(dev->vblank_inmodeset, sizeof(*dev->vblank_inmodeset) *
dev->num_crtcs, DRM_MEM_DRIVER);
@@ -161,6 +164,11 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
if (!dev->last_vblank)
goto err;
+ dev->last_vblank_wait = drm_calloc(num_crtcs, sizeof(u32),
+ DRM_MEM_DRIVER);
+ if (!dev->last_vblank_wait)
+ goto err;
+
dev->vblank_inmodeset = drm_calloc(num_crtcs, sizeof(int),
DRM_MEM_DRIVER);
if (!dev->vblank_inmodeset)
@@ -305,6 +313,8 @@ int drm_control(struct drm_device *dev, void *data,
case DRM_INST_HANDLER:
if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
return 0;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
if (dev->if_version < DRM_IF_VERSION(1, 2) &&
ctl->irq != dev->pdev->irq)
return -EINVAL;
@@ -312,6 +322,8 @@ int drm_control(struct drm_device *dev, void *data,
case DRM_UNINST_HANDLER:
if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
return 0;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
return drm_irq_uninstall(dev);
default:
return -EINVAL;
@@ -427,6 +439,45 @@ void drm_vblank_put(struct drm_device *dev, int crtc)
EXPORT_SYMBOL(drm_vblank_put);
/**
+ * drm_vblank_pre_modeset - account for vblanks across mode sets
+ * @dev: DRM device
+ * @crtc: CRTC in question
+ * @post: post or pre mode set?
+ *
+ * Account for vblank events across mode setting events, which will likely
+ * reset the hardware frame counter.
+ */
+void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
+{
+ /*
+ * To avoid all the problems that might happen if interrupts
+ * were enabled/disabled around or between these calls, we just
+ * have the kernel take a reference on the CRTC (just once though
+ * to avoid corrupting the count if multiple, mismatch calls occur),
+ * so that interrupts remain enabled in the interim.
+ */
+ if (!dev->vblank_inmodeset[crtc]) {
+ dev->vblank_inmodeset[crtc] = 1;
+ drm_vblank_get(dev, crtc);
+ }
+}
+EXPORT_SYMBOL(drm_vblank_pre_modeset);
+
+void drm_vblank_post_modeset(struct drm_device *dev, int crtc)
+{
+ unsigned long irqflags;
+
+ if (dev->vblank_inmodeset[crtc]) {
+ spin_lock_irqsave(&dev->vbl_lock, irqflags);
+ dev->vblank_disable_allowed = 1;
+ dev->vblank_inmodeset[crtc] = 0;
+ spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+ drm_vblank_put(dev, crtc);
+ }
+}
+EXPORT_SYMBOL(drm_vblank_post_modeset);
+
+/**
* drm_modeset_ctl - handle vblank event counter changes across mode switch
* @DRM_IOCTL_ARGS: standard ioctl arguments
*
@@ -441,7 +492,6 @@ int drm_modeset_ctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_modeset_ctl *modeset = data;
- unsigned long irqflags;
int crtc, ret = 0;
/* If drm_vblank_init() hasn't been called yet, just no-op */
@@ -454,28 +504,12 @@ int drm_modeset_ctl(struct drm_device *dev, void *data,
goto out;
}
- /*
- * To avoid all the problems that might happen if interrupts
- * were enabled/disabled around or between these calls, we just
- * have the kernel take a reference on the CRTC (just once though
- * to avoid corrupting the count if multiple, mismatch calls occur),
- * so that interrupts remain enabled in the interim.
- */
switch (modeset->cmd) {
case _DRM_PRE_MODESET:
- if (!dev->vblank_inmodeset[crtc]) {
- dev->vblank_inmodeset[crtc] = 1;
- drm_vblank_get(dev, crtc);
- }
+ drm_vblank_pre_modeset(dev, crtc);
break;
case _DRM_POST_MODESET:
- if (dev->vblank_inmodeset[crtc]) {
- spin_lock_irqsave(&dev->vbl_lock, irqflags);
- dev->vblank_disable_allowed = 1;
- dev->vblank_inmodeset[crtc] = 0;
- spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
- drm_vblank_put(dev, crtc);
- }
+ drm_vblank_post_modeset(dev, crtc);
break;
default:
ret = -EINVAL;
@@ -616,6 +650,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
} else {
DRM_DEBUG("waiting on vblank count %d, crtc %d\n",
vblwait->request.sequence, crtc);
+ dev->last_vblank_wait[crtc] = vblwait->request.sequence;
DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ,
((drm_vblank_count(dev, crtc)
- vblwait->request.sequence) <= (1 << 23)));
diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c
index 1cfa72031f8f..46e7b28f0707 100644
--- a/drivers/gpu/drm/drm_lock.c
+++ b/drivers/gpu/drm/drm_lock.c
@@ -52,6 +52,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
DECLARE_WAITQUEUE(entry, current);
struct drm_lock *lock = data;
+ struct drm_master *master = file_priv->master;
int ret = 0;
++file_priv->lock_count;
@@ -64,26 +65,27 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
lock->context, task_pid_nr(current),
- dev->lock.hw_lock->lock, lock->flags);
+ master->lock.hw_lock->lock, lock->flags);
if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE))
if (lock->context < 0)
return -EINVAL;
- add_wait_queue(&dev->lock.lock_queue, &entry);
- spin_lock_bh(&dev->lock.spinlock);
- dev->lock.user_waiters++;
- spin_unlock_bh(&dev->lock.spinlock);
+ add_wait_queue(&master->lock.lock_queue, &entry);
+ spin_lock_bh(&master->lock.spinlock);
+ master->lock.user_waiters++;
+ spin_unlock_bh(&master->lock.spinlock);
+
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
- if (!dev->lock.hw_lock) {
+ if (!master->lock.hw_lock) {
/* Device has been unregistered */
ret = -EINTR;
break;
}
- if (drm_lock_take(&dev->lock, lock->context)) {
- dev->lock.file_priv = file_priv;
- dev->lock.lock_time = jiffies;
+ if (drm_lock_take(&master->lock, lock->context)) {
+ master->lock.file_priv = file_priv;
+ master->lock.lock_time = jiffies;
atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
break; /* Got lock */
}
@@ -95,11 +97,11 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
break;
}
}
- spin_lock_bh(&dev->lock.spinlock);
- dev->lock.user_waiters--;
- spin_unlock_bh(&dev->lock.spinlock);
+ spin_lock_bh(&master->lock.spinlock);
+ master->lock.user_waiters--;
+ spin_unlock_bh(&master->lock.spinlock);
__set_current_state(TASK_RUNNING);
- remove_wait_queue(&dev->lock.lock_queue, &entry);
+ remove_wait_queue(&master->lock.lock_queue, &entry);
DRM_DEBUG("%d %s\n", lock->context,
ret ? "interrupted" : "has lock");
@@ -108,14 +110,14 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
/* don't set the block all signals on the master process for now
* really probably not the correct answer but lets us debug xkb
* xserver for now */
- if (!file_priv->master) {
+ if (!file_priv->is_master) {
sigemptyset(&dev->sigmask);
sigaddset(&dev->sigmask, SIGSTOP);
sigaddset(&dev->sigmask, SIGTSTP);
sigaddset(&dev->sigmask, SIGTTIN);
sigaddset(&dev->sigmask, SIGTTOU);
dev->sigdata.context = lock->context;
- dev->sigdata.lock = dev->lock.hw_lock;
+ dev->sigdata.lock = master->lock.hw_lock;
block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
}
@@ -154,6 +156,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct drm_lock *lock = data;
+ struct drm_master *master = file_priv->master;
if (lock->context == DRM_KERNEL_CONTEXT) {
DRM_ERROR("Process %d using kernel context %d\n",
@@ -169,7 +172,7 @@ int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
if (dev->driver->kernel_context_switch_unlock)
dev->driver->kernel_context_switch_unlock(dev);
else {
- if (drm_lock_free(&dev->lock,lock->context)) {
+ if (drm_lock_free(&master->lock, lock->context)) {
/* FIXME: Should really bail out here. */
}
}
@@ -379,9 +382,10 @@ EXPORT_SYMBOL(drm_idlelock_release);
int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv)
{
- return (file_priv->lock_count && dev->lock.hw_lock &&
- _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
- dev->lock.file_priv == file_priv);
+ struct drm_master *master = file_priv->master;
+ return (file_priv->lock_count && master->lock.hw_lock &&
+ _DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) &&
+ master->lock.file_priv == file_priv);
}
EXPORT_SYMBOL(drm_i_have_hw_lock);
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index 217ad7dc7076..367c590ffbba 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -296,3 +296,4 @@ void drm_mm_takedown(struct drm_mm * mm)
drm_free(entry, sizeof(*entry), DRM_MEM_MM);
}
+EXPORT_SYMBOL(drm_mm_takedown);
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
new file mode 100644
index 000000000000..c9b80fdd4630
--- /dev/null
+++ b/drivers/gpu/drm/drm_modes.c
@@ -0,0 +1,576 @@
+/*
+ * The list_sort function is (presumably) licensed under the GPL (see the
+ * top level "COPYING" file for details).
+ *
+ * The remainder of this file is:
+ *
+ * Copyright © 1997-2003 by The XFree86 Project, Inc.
+ * Copyright © 2007 Dave Airlie
+ * Copyright © 2007-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Except as contained in this notice, the name of the copyright holder(s)
+ * and author(s) shall not be used in advertising or otherwise to promote
+ * the sale, use or other dealings in this Software without prior written
+ * authorization from the copyright holder(s) and author(s).
+ */
+
+#include <linux/list.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+
+/**
+ * drm_mode_debug_printmodeline - debug print a mode
+ * @dev: DRM device
+ * @mode: mode to print
+ *
+ * LOCKING:
+ * None.
+ *
+ * Describe @mode using DRM_DEBUG.
+ */
+void drm_mode_debug_printmodeline(struct drm_display_mode *mode)
+{
+ DRM_DEBUG("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
+ mode->base.id, mode->name, mode->vrefresh, mode->clock,
+ mode->hdisplay, mode->hsync_start,
+ mode->hsync_end, mode->htotal,
+ mode->vdisplay, mode->vsync_start,
+ mode->vsync_end, mode->vtotal, mode->type, mode->flags);
+}
+EXPORT_SYMBOL(drm_mode_debug_printmodeline);
+
+/**
+ * drm_mode_set_name - set the name on a mode
+ * @mode: name will be set in this mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Set the name of @mode to a standard format.
+ */
+void drm_mode_set_name(struct drm_display_mode *mode)
+{
+ snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay,
+ mode->vdisplay);
+}
+EXPORT_SYMBOL(drm_mode_set_name);
+
+/**
+ * drm_mode_list_concat - move modes from one list to another
+ * @head: source list
+ * @new: dst list
+ *
+ * LOCKING:
+ * Caller must ensure both lists are locked.
+ *
+ * Move all the modes from @head to @new.
+ */
+void drm_mode_list_concat(struct list_head *head, struct list_head *new)
+{
+
+ struct list_head *entry, *tmp;
+
+ list_for_each_safe(entry, tmp, head) {
+ list_move_tail(entry, new);
+ }
+}
+EXPORT_SYMBOL(drm_mode_list_concat);
+
+/**
+ * drm_mode_width - get the width of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @mode's width (hdisplay) value.
+ *
+ * FIXME: is this needed?
+ *
+ * RETURNS:
+ * @mode->hdisplay
+ */
+int drm_mode_width(struct drm_display_mode *mode)
+{
+ return mode->hdisplay;
+
+}
+EXPORT_SYMBOL(drm_mode_width);
+
+/**
+ * drm_mode_height - get the height of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @mode's height (vdisplay) value.
+ *
+ * FIXME: is this needed?
+ *
+ * RETURNS:
+ * @mode->vdisplay
+ */
+int drm_mode_height(struct drm_display_mode *mode)
+{
+ return mode->vdisplay;
+}
+EXPORT_SYMBOL(drm_mode_height);
+
+/**
+ * drm_mode_vrefresh - get the vrefresh of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @mode's vrefresh rate or calculate it if necessary.
+ *
+ * FIXME: why is this needed? shouldn't vrefresh be set already?
+ *
+ * RETURNS:
+ * Vertical refresh rate of @mode x 1000. For precision reasons.
+ */
+int drm_mode_vrefresh(struct drm_display_mode *mode)
+{
+ int refresh = 0;
+ unsigned int calc_val;
+
+ if (mode->vrefresh > 0)
+ refresh = mode->vrefresh;
+ else if (mode->htotal > 0 && mode->vtotal > 0) {
+ /* work out vrefresh the value will be x1000 */
+ calc_val = (mode->clock * 1000);
+
+ calc_val /= mode->htotal;
+ calc_val *= 1000;
+ calc_val /= mode->vtotal;
+
+ refresh = calc_val;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ refresh *= 2;
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ refresh /= 2;
+ if (mode->vscan > 1)
+ refresh /= mode->vscan;
+ }
+ return refresh;
+}
+EXPORT_SYMBOL(drm_mode_vrefresh);
+
+/**
+ * drm_mode_set_crtcinfo - set CRTC modesetting parameters
+ * @p: mode
+ * @adjust_flags: unused? (FIXME)
+ *
+ * LOCKING:
+ * None.
+ *
+ * Setup the CRTC modesetting parameters for @p, adjusting if necessary.
+ */
+void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
+{
+ if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN))
+ return;
+
+ p->crtc_hdisplay = p->hdisplay;
+ p->crtc_hsync_start = p->hsync_start;
+ p->crtc_hsync_end = p->hsync_end;
+ p->crtc_htotal = p->htotal;
+ p->crtc_hskew = p->hskew;
+ p->crtc_vdisplay = p->vdisplay;
+ p->crtc_vsync_start = p->vsync_start;
+ p->crtc_vsync_end = p->vsync_end;
+ p->crtc_vtotal = p->vtotal;
+
+ if (p->flags & DRM_MODE_FLAG_INTERLACE) {
+ if (adjust_flags & CRTC_INTERLACE_HALVE_V) {
+ p->crtc_vdisplay /= 2;
+ p->crtc_vsync_start /= 2;
+ p->crtc_vsync_end /= 2;
+ p->crtc_vtotal /= 2;
+ }
+
+ p->crtc_vtotal |= 1;
+ }
+
+ if (p->flags & DRM_MODE_FLAG_DBLSCAN) {
+ p->crtc_vdisplay *= 2;
+ p->crtc_vsync_start *= 2;
+ p->crtc_vsync_end *= 2;
+ p->crtc_vtotal *= 2;
+ }
+
+ if (p->vscan > 1) {
+ p->crtc_vdisplay *= p->vscan;
+ p->crtc_vsync_start *= p->vscan;
+ p->crtc_vsync_end *= p->vscan;
+ p->crtc_vtotal *= p->vscan;
+ }
+
+ p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay);
+ p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal);
+ p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
+ p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal);
+
+ p->crtc_hadjusted = false;
+ p->crtc_vadjusted = false;
+}
+EXPORT_SYMBOL(drm_mode_set_crtcinfo);
+
+
+/**
+ * drm_mode_duplicate - allocate and duplicate an existing mode
+ * @m: mode to duplicate
+ *
+ * LOCKING:
+ * None.
+ *
+ * Just allocate a new mode, copy the existing mode into it, and return
+ * a pointer to it. Used to create new instances of established modes.
+ */
+struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
+ struct drm_display_mode *mode)
+{
+ struct drm_display_mode *nmode;
+ int new_id;
+
+ nmode = drm_mode_create(dev);
+ if (!nmode)
+ return NULL;
+
+ new_id = nmode->base.id;
+ *nmode = *mode;
+ nmode->base.id = new_id;
+ INIT_LIST_HEAD(&nmode->head);
+ return nmode;
+}
+EXPORT_SYMBOL(drm_mode_duplicate);
+
+/**
+ * drm_mode_equal - test modes for equality
+ * @mode1: first mode
+ * @mode2: second mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Check to see if @mode1 and @mode2 are equivalent.
+ *
+ * RETURNS:
+ * True if the modes are equal, false otherwise.
+ */
+bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2)
+{
+ /* do clock check convert to PICOS so fb modes get matched
+ * the same */
+ if (mode1->clock && mode2->clock) {
+ if (KHZ2PICOS(mode1->clock) != KHZ2PICOS(mode2->clock))
+ return false;
+ } else if (mode1->clock != mode2->clock)
+ return false;
+
+ if (mode1->hdisplay == mode2->hdisplay &&
+ mode1->hsync_start == mode2->hsync_start &&
+ mode1->hsync_end == mode2->hsync_end &&
+ mode1->htotal == mode2->htotal &&
+ mode1->hskew == mode2->hskew &&
+ mode1->vdisplay == mode2->vdisplay &&
+ mode1->vsync_start == mode2->vsync_start &&
+ mode1->vsync_end == mode2->vsync_end &&
+ mode1->vtotal == mode2->vtotal &&
+ mode1->vscan == mode2->vscan &&
+ mode1->flags == mode2->flags)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL(drm_mode_equal);
+
+/**
+ * drm_mode_validate_size - make sure modes adhere to size constraints
+ * @dev: DRM device
+ * @mode_list: list of modes to check
+ * @maxX: maximum width
+ * @maxY: maximum height
+ * @maxPitch: max pitch
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * The DRM device (@dev) has size and pitch limits. Here we validate the
+ * modes we probed for @dev against those limits and set their status as
+ * necessary.
+ */
+void drm_mode_validate_size(struct drm_device *dev,
+ struct list_head *mode_list,
+ int maxX, int maxY, int maxPitch)
+{
+ struct drm_display_mode *mode;
+
+ list_for_each_entry(mode, mode_list, head) {
+ if (maxPitch > 0 && mode->hdisplay > maxPitch)
+ mode->status = MODE_BAD_WIDTH;
+
+ if (maxX > 0 && mode->hdisplay > maxX)
+ mode->status = MODE_VIRTUAL_X;
+
+ if (maxY > 0 && mode->vdisplay > maxY)
+ mode->status = MODE_VIRTUAL_Y;
+ }
+}
+EXPORT_SYMBOL(drm_mode_validate_size);
+
+/**
+ * drm_mode_validate_clocks - validate modes against clock limits
+ * @dev: DRM device
+ * @mode_list: list of modes to check
+ * @min: minimum clock rate array
+ * @max: maximum clock rate array
+ * @n_ranges: number of clock ranges (size of arrays)
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * Some code may need to check a mode list against the clock limits of the
+ * device in question. This function walks the mode list, testing to make
+ * sure each mode falls within a given range (defined by @min and @max
+ * arrays) and sets @mode->status as needed.
+ */
+void drm_mode_validate_clocks(struct drm_device *dev,
+ struct list_head *mode_list,
+ int *min, int *max, int n_ranges)
+{
+ struct drm_display_mode *mode;
+ int i;
+
+ list_for_each_entry(mode, mode_list, head) {
+ bool good = false;
+ for (i = 0; i < n_ranges; i++) {
+ if (mode->clock >= min[i] && mode->clock <= max[i]) {
+ good = true;
+ break;
+ }
+ }
+ if (!good)
+ mode->status = MODE_CLOCK_RANGE;
+ }
+}
+EXPORT_SYMBOL(drm_mode_validate_clocks);
+
+/**
+ * drm_mode_prune_invalid - remove invalid modes from mode list
+ * @dev: DRM device
+ * @mode_list: list of modes to check
+ * @verbose: be verbose about it
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * Once mode list generation is complete, a caller can use this routine to
+ * remove invalid modes from a mode list. If any of the modes have a
+ * status other than %MODE_OK, they are removed from @mode_list and freed.
+ */
+void drm_mode_prune_invalid(struct drm_device *dev,
+ struct list_head *mode_list, bool verbose)
+{
+ struct drm_display_mode *mode, *t;
+
+ list_for_each_entry_safe(mode, t, mode_list, head) {
+ if (mode->status != MODE_OK) {
+ list_del(&mode->head);
+ if (verbose) {
+ drm_mode_debug_printmodeline(mode);
+ DRM_DEBUG("Not using %s mode %d\n", mode->name, mode->status);
+ }
+ drm_mode_destroy(dev, mode);
+ }
+ }
+}
+EXPORT_SYMBOL(drm_mode_prune_invalid);
+
+/**
+ * drm_mode_compare - compare modes for favorability
+ * @lh_a: list_head for first mode
+ * @lh_b: list_head for second mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Compare two modes, given by @lh_a and @lh_b, returning a value indicating
+ * which is better.
+ *
+ * RETURNS:
+ * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or
+ * positive if @lh_b is better than @lh_a.
+ */
+static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b)
+{
+ struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head);
+ struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head);
+ int diff;
+
+ diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) -
+ ((a->type & DRM_MODE_TYPE_PREFERRED) != 0);
+ if (diff)
+ return diff;
+ diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay;
+ if (diff)
+ return diff;
+ diff = b->clock - a->clock;
+ return diff;
+}
+
+/* FIXME: what we don't have a list sort function? */
+/* list sort from Mark J Roberts (mjr@znex.org) */
+void list_sort(struct list_head *head,
+ int (*cmp)(struct list_head *a, struct list_head *b))
+{
+ struct list_head *p, *q, *e, *list, *tail, *oldhead;
+ int insize, nmerges, psize, qsize, i;
+
+ list = head->next;
+ list_del(head);
+ insize = 1;
+ for (;;) {
+ p = oldhead = list;
+ list = tail = NULL;
+ nmerges = 0;
+
+ while (p) {
+ nmerges++;
+ q = p;
+ psize = 0;
+ for (i = 0; i < insize; i++) {
+ psize++;
+ q = q->next == oldhead ? NULL : q->next;
+ if (!q)
+ break;
+ }
+
+ qsize = insize;
+ while (psize > 0 || (qsize > 0 && q)) {
+ if (!psize) {
+ e = q;
+ q = q->next;
+ qsize--;
+ if (q == oldhead)
+ q = NULL;
+ } else if (!qsize || !q) {
+ e = p;
+ p = p->next;
+ psize--;
+ if (p == oldhead)
+ p = NULL;
+ } else if (cmp(p, q) <= 0) {
+ e = p;
+ p = p->next;
+ psize--;
+ if (p == oldhead)
+ p = NULL;
+ } else {
+ e = q;
+ q = q->next;
+ qsize--;
+ if (q == oldhead)
+ q = NULL;
+ }
+ if (tail)
+ tail->next = e;
+ else
+ list = e;
+ e->prev = tail;
+ tail = e;
+ }
+ p = q;
+ }
+
+ tail->next = list;
+ list->prev = tail;
+
+ if (nmerges <= 1)
+ break;
+
+ insize *= 2;
+ }
+
+ head->next = list;
+ head->prev = list->prev;
+ list->prev->next = head;
+ list->prev = head;
+}
+
+/**
+ * drm_mode_sort - sort mode list
+ * @mode_list: list to sort
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * Sort @mode_list by favorability, putting good modes first.
+ */
+void drm_mode_sort(struct list_head *mode_list)
+{
+ list_sort(mode_list, drm_mode_compare);
+}
+EXPORT_SYMBOL(drm_mode_sort);
+
+/**
+ * drm_mode_connector_list_update - update the mode list for the connector
+ * @connector: the connector to update
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * This moves the modes from the @connector probed_modes list
+ * to the actual mode list. It compares the probed mode against the current
+ * list and only adds different modes. All modes unverified after this point
+ * will be removed by the prune invalid modes.
+ */
+void drm_mode_connector_list_update(struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+ struct drm_display_mode *pmode, *pt;
+ int found_it;
+
+ list_for_each_entry_safe(pmode, pt, &connector->probed_modes,
+ head) {
+ found_it = 0;
+ /* go through current modes checking for the new probed mode */
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (drm_mode_equal(pmode, mode)) {
+ found_it = 1;
+ /* if equal delete the probed mode */
+ mode->status = pmode->status;
+ list_del(&pmode->head);
+ drm_mode_destroy(connector->dev, pmode);
+ break;
+ }
+ }
+
+ if (!found_it) {
+ list_move_tail(&pmode->head, &connector->modes);
+ }
+ }
+}
+EXPORT_SYMBOL(drm_mode_connector_list_update);
diff --git a/drivers/gpu/drm/drm_proc.c b/drivers/gpu/drm/drm_proc.c
index ae73b7f7249a..8df849f66830 100644
--- a/drivers/gpu/drm/drm_proc.c
+++ b/drivers/gpu/drm/drm_proc.c
@@ -49,6 +49,8 @@ static int drm_queues_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data);
static int drm_bufs_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data);
+static int drm_vblank_info(char *buf, char **start, off_t offset,
+ int request, int *eof, void *data);
static int drm_gem_name_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data);
static int drm_gem_object_info(char *buf, char **start, off_t offset,
@@ -72,6 +74,7 @@ static struct drm_proc_list {
{"clients", drm_clients_info, 0},
{"queues", drm_queues_info, 0},
{"bufs", drm_bufs_info, 0},
+ {"vblank", drm_vblank_info, 0},
{"gem_names", drm_gem_name_info, DRIVER_GEM},
{"gem_objects", drm_gem_object_info, DRIVER_GEM},
#if DRM_DEBUG_CODE
@@ -195,6 +198,7 @@ static int drm_name_info(char *buf, char **start, off_t offset, int request,
int *eof, void *data)
{
struct drm_minor *minor = (struct drm_minor *) data;
+ struct drm_master *master = minor->master;
struct drm_device *dev = minor->dev;
int len = 0;
@@ -203,13 +207,16 @@ static int drm_name_info(char *buf, char **start, off_t offset, int request,
return 0;
}
+ if (!master)
+ return 0;
+
*start = &buf[offset];
*eof = 0;
- if (dev->unique) {
+ if (master->unique) {
DRM_PROC_PRINT("%s %s %s\n",
dev->driver->pci_driver.name,
- pci_name(dev->pdev), dev->unique);
+ pci_name(dev->pdev), master->unique);
} else {
DRM_PROC_PRINT("%s %s\n", dev->driver->pci_driver.name,
pci_name(dev->pdev));
@@ -454,6 +461,66 @@ static int drm_bufs_info(char *buf, char **start, off_t offset, int request,
}
/**
+ * Called when "/proc/dri/.../vblank" is read.
+ *
+ * \param buf output buffer.
+ * \param start start of output data.
+ * \param offset requested start offset.
+ * \param request requested number of bytes.
+ * \param eof whether there is no more data to return.
+ * \param data private data.
+ * \return number of written bytes.
+ */
+static int drm__vblank_info(char *buf, char **start, off_t offset, int request,
+ int *eof, void *data)
+{
+ struct drm_minor *minor = (struct drm_minor *) data;
+ struct drm_device *dev = minor->dev;
+ int len = 0;
+ int crtc;
+
+ if (offset > DRM_PROC_LIMIT) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = &buf[offset];
+ *eof = 0;
+
+ for (crtc = 0; crtc < dev->num_crtcs; crtc++) {
+ DRM_PROC_PRINT("CRTC %d enable: %d\n",
+ crtc, atomic_read(&dev->vblank_refcount[crtc]));
+ DRM_PROC_PRINT("CRTC %d counter: %d\n",
+ crtc, drm_vblank_count(dev, crtc));
+ DRM_PROC_PRINT("CRTC %d last wait: %d\n",
+ crtc, dev->last_vblank_wait[crtc]);
+ DRM_PROC_PRINT("CRTC %d in modeset: %d\n",
+ crtc, dev->vblank_inmodeset[crtc]);
+ }
+
+ if (len > request + offset)
+ return request;
+ *eof = 1;
+ return len - offset;
+}
+
+/**
+ * Simply calls _vblank_info() while holding the drm_device::struct_mutex lock.
+ */
+static int drm_vblank_info(char *buf, char **start, off_t offset, int request,
+ int *eof, void *data)
+{
+ struct drm_minor *minor = (struct drm_minor *) data;
+ struct drm_device *dev = minor->dev;
+ int ret;
+
+ mutex_lock(&dev->struct_mutex);
+ ret = drm__vblank_info(buf, start, offset, request, eof, data);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+/**
* Called when "/proc/dri/.../clients" is read.
*
* \param buf output buffer.
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index 66c96ec66672..5ca132afa4f2 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -57,6 +57,14 @@ static int drm_minor_get_id(struct drm_device *dev, int type)
int ret;
int base = 0, limit = 63;
+ if (type == DRM_MINOR_CONTROL) {
+ base += 64;
+ limit = base + 127;
+ } else if (type == DRM_MINOR_RENDER) {
+ base += 128;
+ limit = base + 255;
+ }
+
again:
if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) {
DRM_ERROR("Out of memory expanding drawable idr\n");
@@ -79,6 +87,104 @@ again:
return new_id;
}
+struct drm_master *drm_master_create(struct drm_minor *minor)
+{
+ struct drm_master *master;
+
+ master = drm_calloc(1, sizeof(*master), DRM_MEM_DRIVER);
+ if (!master)
+ return NULL;
+
+ kref_init(&master->refcount);
+ spin_lock_init(&master->lock.spinlock);
+ init_waitqueue_head(&master->lock.lock_queue);
+ drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER);
+ INIT_LIST_HEAD(&master->magicfree);
+ master->minor = minor;
+
+ list_add_tail(&master->head, &minor->master_list);
+
+ return master;
+}
+
+struct drm_master *drm_master_get(struct drm_master *master)
+{
+ kref_get(&master->refcount);
+ return master;
+}
+
+static void drm_master_destroy(struct kref *kref)
+{
+ struct drm_master *master = container_of(kref, struct drm_master, refcount);
+ struct drm_magic_entry *pt, *next;
+ struct drm_device *dev = master->minor->dev;
+
+ list_del(&master->head);
+
+ if (dev->driver->master_destroy)
+ dev->driver->master_destroy(dev, master);
+
+ if (master->unique) {
+ drm_free(master->unique, master->unique_size, DRM_MEM_DRIVER);
+ master->unique = NULL;
+ master->unique_len = 0;
+ }
+
+ list_for_each_entry_safe(pt, next, &master->magicfree, head) {
+ list_del(&pt->head);
+ drm_ht_remove_item(&master->magiclist, &pt->hash_item);
+ drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+ }
+
+ drm_ht_remove(&master->magiclist);
+
+ if (master->lock.hw_lock) {
+ if (dev->sigdata.lock == master->lock.hw_lock)
+ dev->sigdata.lock = NULL;
+ master->lock.hw_lock = NULL;
+ master->lock.file_priv = NULL;
+ wake_up_interruptible(&master->lock.lock_queue);
+ }
+
+ drm_free(master, sizeof(*master), DRM_MEM_DRIVER);
+}
+
+void drm_master_put(struct drm_master **master)
+{
+ kref_put(&(*master)->refcount, drm_master_destroy);
+ *master = NULL;
+}
+
+int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ if (file_priv->minor->master && file_priv->minor->master != file_priv->master)
+ return -EINVAL;
+
+ if (!file_priv->master)
+ return -EINVAL;
+
+ if (!file_priv->minor->master &&
+ file_priv->minor->master != file_priv->master) {
+ mutex_lock(&dev->struct_mutex);
+ file_priv->minor->master = drm_master_get(file_priv->master);
+ mutex_lock(&dev->struct_mutex);
+ }
+
+ return 0;
+}
+
+int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ if (!file_priv->master)
+ return -EINVAL;
+ mutex_lock(&dev->struct_mutex);
+ drm_master_put(&file_priv->minor->master);
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+}
+
static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
const struct pci_device_id *ent,
struct drm_driver *driver)
@@ -92,7 +198,6 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
spin_lock_init(&dev->count_lock);
spin_lock_init(&dev->drw_lock);
- spin_lock_init(&dev->lock.spinlock);
init_timer(&dev->timer);
mutex_init(&dev->struct_mutex);
mutex_init(&dev->ctxlist_mutex);
@@ -140,9 +245,6 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
}
}
- if (dev->driver->load)
- if ((retcode = dev->driver->load(dev, ent->driver_data)))
- goto error_out_unreg;
retcode = drm_ctxbitmap_init(dev);
if (retcode) {
@@ -200,6 +302,7 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t
new_minor->device = MKDEV(DRM_MAJOR, minor_id);
new_minor->dev = dev;
new_minor->index = minor_id;
+ INIT_LIST_HEAD(&new_minor->master_list);
idr_replace(&drm_minors_idr, new_minor, minor_id);
@@ -267,8 +370,30 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
goto err_g2;
}
+
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL);
+ if (ret)
+ goto err_g2;
+ }
+
if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY)))
- goto err_g2;
+ goto err_g3;
+
+ if (dev->driver->load) {
+ ret = dev->driver->load(dev, ent->driver_data);
+ if (ret)
+ goto err_g3;
+ }
+
+ /* setup the grouping for the legacy output */
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ ret = drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
+ if (ret)
+ goto err_g3;
+ }
+
+ list_add_tail(&dev->driver_item, &driver->device_list);
DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
driver->name, driver->major, driver->minor, driver->patchlevel,
@@ -276,6 +401,8 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
return 0;
+err_g3:
+ drm_put_minor(&dev->primary);
err_g2:
pci_disable_device(pdev);
err_g1:
@@ -297,11 +424,6 @@ int drm_put_dev(struct drm_device * dev)
{
DRM_DEBUG("release primary %s\n", dev->driver->pci_driver.name);
- if (dev->unique) {
- drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER);
- dev->unique = NULL;
- dev->unique_len = 0;
- }
if (dev->devname) {
drm_free(dev->devname, strlen(dev->devname) + 1,
DRM_MEM_DRIVER);
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 1611b9bcbe7f..65d72d094c81 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -20,6 +20,7 @@
#include "drmP.h"
#define to_drm_minor(d) container_of(d, struct drm_minor, kdev)
+#define to_drm_connector(d) container_of(d, struct drm_connector, kdev)
/**
* drm_sysfs_suspend - DRM class suspend hook
@@ -34,7 +35,7 @@ static int drm_sysfs_suspend(struct device *dev, pm_message_t state)
struct drm_minor *drm_minor = to_drm_minor(dev);
struct drm_device *drm_dev = drm_minor->dev;
- if (drm_dev->driver->suspend)
+ if (drm_minor->type == DRM_MINOR_LEGACY && drm_dev->driver->suspend)
return drm_dev->driver->suspend(drm_dev, state);
return 0;
@@ -52,7 +53,7 @@ static int drm_sysfs_resume(struct device *dev)
struct drm_minor *drm_minor = to_drm_minor(dev);
struct drm_device *drm_dev = drm_minor->dev;
- if (drm_dev->driver->resume)
+ if (drm_minor->type == DRM_MINOR_LEGACY && drm_dev->driver->resume)
return drm_dev->driver->resume(drm_dev);
return 0;
@@ -144,6 +145,323 @@ static void drm_sysfs_device_release(struct device *dev)
return;
}
+/*
+ * Connector properties
+ */
+static ssize_t status_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ enum drm_connector_status status;
+
+ status = connector->funcs->detect(connector);
+ return snprintf(buf, PAGE_SIZE, "%s",
+ drm_get_connector_status_name(status));
+}
+
+static ssize_t dpms_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ struct drm_device *dev = connector->dev;
+ uint64_t dpms_status;
+ int ret;
+
+ ret = drm_connector_property_get_value(connector,
+ dev->mode_config.dpms_property,
+ &dpms_status);
+ if (ret)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s",
+ drm_get_dpms_name((int)dpms_status));
+}
+
+static ssize_t enabled_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+
+ return snprintf(buf, PAGE_SIZE, connector->encoder ? "enabled" :
+ "disabled");
+}
+
+static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *connector_dev = container_of(kobj, struct device, kobj);
+ struct drm_connector *connector = to_drm_connector(connector_dev);
+ unsigned char *edid;
+ size_t size;
+
+ if (!connector->edid_blob_ptr)
+ return 0;
+
+ edid = connector->edid_blob_ptr->data;
+ size = connector->edid_blob_ptr->length;
+ if (!edid)
+ return 0;
+
+ if (off >= size)
+ return 0;
+
+ if (off + count > size)
+ count = size - off;
+ memcpy(buf, edid + off, count);
+
+ return count;
+}
+
+static ssize_t modes_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ struct drm_display_mode *mode;
+ int written = 0;
+
+ list_for_each_entry(mode, &connector->modes, head) {
+ written += snprintf(buf + written, PAGE_SIZE - written, "%s\n",
+ mode->name);
+ }
+
+ return written;
+}
+
+static ssize_t subconnector_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ struct drm_device *dev = connector->dev;
+ struct drm_property *prop = NULL;
+ uint64_t subconnector;
+ int is_tv = 0;
+ int ret;
+
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_DVII:
+ prop = dev->mode_config.dvi_i_subconnector_property;
+ break;
+ case DRM_MODE_CONNECTOR_Composite:
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_Component:
+ prop = dev->mode_config.tv_subconnector_property;
+ is_tv = 1;
+ break;
+ default:
+ DRM_ERROR("Wrong connector type for this property\n");
+ return 0;
+ }
+
+ if (!prop) {
+ DRM_ERROR("Unable to find subconnector property\n");
+ return 0;
+ }
+
+ ret = drm_connector_property_get_value(connector, prop, &subconnector);
+ if (ret)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s", is_tv ?
+ drm_get_tv_subconnector_name((int)subconnector) :
+ drm_get_dvi_i_subconnector_name((int)subconnector));
+}
+
+static ssize_t select_subconnector_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ struct drm_device *dev = connector->dev;
+ struct drm_property *prop = NULL;
+ uint64_t subconnector;
+ int is_tv = 0;
+ int ret;
+
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_DVII:
+ prop = dev->mode_config.dvi_i_select_subconnector_property;
+ break;
+ case DRM_MODE_CONNECTOR_Composite:
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_Component:
+ prop = dev->mode_config.tv_select_subconnector_property;
+ is_tv = 1;
+ break;
+ default:
+ DRM_ERROR("Wrong connector type for this property\n");
+ return 0;
+ }
+
+ if (!prop) {
+ DRM_ERROR("Unable to find select subconnector property\n");
+ return 0;
+ }
+
+ ret = drm_connector_property_get_value(connector, prop, &subconnector);
+ if (ret)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s", is_tv ?
+ drm_get_tv_select_name((int)subconnector) :
+ drm_get_dvi_i_select_name((int)subconnector));
+}
+
+static struct device_attribute connector_attrs[] = {
+ __ATTR_RO(status),
+ __ATTR_RO(enabled),
+ __ATTR_RO(dpms),
+ __ATTR_RO(modes),
+};
+
+/* These attributes are for both DVI-I connectors and all types of tv-out. */
+static struct device_attribute connector_attrs_opt1[] = {
+ __ATTR_RO(subconnector),
+ __ATTR_RO(select_subconnector),
+};
+
+static struct bin_attribute edid_attr = {
+ .attr.name = "edid",
+ .size = 128,
+ .read = edid_show,
+};
+
+/**
+ * drm_sysfs_connector_add - add an connector to sysfs
+ * @connector: connector to add
+ *
+ * Create an connector device in sysfs, along with its associated connector
+ * properties (so far, connection status, dpms, mode list & edid) and
+ * generate a hotplug event so userspace knows there's a new connector
+ * available.
+ *
+ * Note:
+ * This routine should only be called *once* for each DRM minor registered.
+ * A second call for an already registered device will trigger the BUG_ON
+ * below.
+ */
+int drm_sysfs_connector_add(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ int ret = 0, i, j;
+
+ /* We shouldn't get called more than once for the same connector */
+ BUG_ON(device_is_registered(&connector->kdev));
+
+ connector->kdev.parent = &dev->primary->kdev;
+ connector->kdev.class = drm_class;
+ connector->kdev.release = drm_sysfs_device_release;
+
+ DRM_DEBUG("adding \"%s\" to sysfs\n",
+ drm_get_connector_name(connector));
+
+ snprintf(connector->kdev.bus_id, BUS_ID_SIZE, "card%d-%s",
+ dev->primary->index, drm_get_connector_name(connector));
+ ret = device_register(&connector->kdev);
+
+ if (ret) {
+ DRM_ERROR("failed to register connector device: %d\n", ret);
+ goto out;
+ }
+
+ /* Standard attributes */
+
+ for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) {
+ ret = device_create_file(&connector->kdev, &connector_attrs[i]);
+ if (ret)
+ goto err_out_files;
+ }
+
+ /* Optional attributes */
+ /*
+ * In the long run it maybe a good idea to make one set of
+ * optionals per connector type.
+ */
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_DVII:
+ case DRM_MODE_CONNECTOR_Composite:
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_Component:
+ for (i = 0; i < ARRAY_SIZE(connector_attrs_opt1); i++) {
+ ret = device_create_file(&connector->kdev, &connector_attrs_opt1[i]);
+ if (ret)
+ goto err_out_files;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr);
+ if (ret)
+ goto err_out_files;
+
+ /* Let userspace know we have a new connector */
+ drm_sysfs_hotplug_event(dev);
+
+ return 0;
+
+err_out_files:
+ if (i > 0)
+ for (j = 0; j < i; j++)
+ device_remove_file(&connector->kdev,
+ &connector_attrs[i]);
+ device_unregister(&connector->kdev);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL(drm_sysfs_connector_add);
+
+/**
+ * drm_sysfs_connector_remove - remove an connector device from sysfs
+ * @connector: connector to remove
+ *
+ * Remove @connector and its associated attributes from sysfs. Note that
+ * the device model core will take care of sending the "remove" uevent
+ * at this time, so we don't need to do it.
+ *
+ * Note:
+ * This routine should only be called if the connector was previously
+ * successfully registered. If @connector hasn't been registered yet,
+ * you'll likely see a panic somewhere deep in sysfs code when called.
+ */
+void drm_sysfs_connector_remove(struct drm_connector *connector)
+{
+ int i;
+
+ DRM_DEBUG("removing \"%s\" from sysfs\n",
+ drm_get_connector_name(connector));
+
+ for (i = 0; i < ARRAY_SIZE(connector_attrs); i++)
+ device_remove_file(&connector->kdev, &connector_attrs[i]);
+ sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr);
+ device_unregister(&connector->kdev);
+}
+EXPORT_SYMBOL(drm_sysfs_connector_remove);
+
+/**
+ * drm_sysfs_hotplug_event - generate a DRM uevent
+ * @dev: DRM device
+ *
+ * Send a uevent for the DRM device specified by @dev. Currently we only
+ * set HOTPLUG=1 in the uevent environment, but this could be expanded to
+ * deal with other types of events.
+ */
+void drm_sysfs_hotplug_event(struct drm_device *dev)
+{
+ char *event_string = "HOTPLUG=1";
+ char *envp[] = { event_string, NULL };
+
+ DRM_DEBUG("generating hotplug event\n");
+
+ kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp);
+}
+
/**
* drm_sysfs_device_add - adds a class device to sysfs for a character driver
* @dev: DRM device to be added
@@ -163,7 +481,12 @@ int drm_sysfs_device_add(struct drm_minor *minor)
minor->kdev.class = drm_class;
minor->kdev.release = drm_sysfs_device_release;
minor->kdev.devt = minor->device;
- minor_str = "card%d";
+ if (minor->type == DRM_MINOR_CONTROL)
+ minor_str = "controlD%d";
+ else if (minor->type == DRM_MINOR_RENDER)
+ minor_str = "renderD%d";
+ else
+ minor_str = "card%d";
snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index);
diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c
index c234c6f24a8d..3ffae021d280 100644
--- a/drivers/gpu/drm/drm_vm.c
+++ b/drivers/gpu/drm/drm_vm.c
@@ -267,6 +267,9 @@ static void drm_vm_shm_close(struct vm_area_struct *vma)
dmah.size = map->size;
__drm_pci_free(dev, &dmah);
break;
+ case _DRM_GEM:
+ DRM_ERROR("tried to rmmap GEM object\n");
+ break;
}
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
}
@@ -399,7 +402,7 @@ static struct vm_operations_struct drm_vm_sg_ops = {
* Create a new drm_vma_entry structure as the \p vma private data entry and
* add it to drm_device::vmalist.
*/
-static void drm_vm_open_locked(struct vm_area_struct *vma)
+void drm_vm_open_locked(struct vm_area_struct *vma)
{
struct drm_file *priv = vma->vm_file->private_data;
struct drm_device *dev = priv->minor->dev;
@@ -540,7 +543,7 @@ EXPORT_SYMBOL(drm_core_get_reg_ofs);
* according to the mapping type and remaps the pages. Finally sets the file
* pointer and calls vm_open().
*/
-static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
+int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *priv = filp->private_data;
struct drm_device *dev = priv->minor->dev;
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index d8fb5d8ee7ea..dd57a5bd4572 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -8,7 +8,22 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
i915_gem.o \
i915_gem_debug.o \
i915_gem_proc.o \
- i915_gem_tiling.o
+ i915_gem_tiling.o \
+ intel_display.o \
+ intel_crt.o \
+ intel_lvds.o \
+ intel_bios.o \
+ intel_sdvo.o \
+ intel_modes.o \
+ intel_i2c.o \
+ intel_fb.o \
+ intel_tv.o \
+ intel_dvo.o \
+ dvo_ch7xxx.o \
+ dvo_ch7017.o \
+ dvo_ivch.o \
+ dvo_tfp410.o \
+ dvo_sil164.o
i915-$(CONFIG_ACPI) += i915_opregion.o
i915-$(CONFIG_COMPAT) += i915_ioc32.o
diff --git a/drivers/gpu/drm/i915/dvo.h b/drivers/gpu/drm/i915/dvo.h
new file mode 100644
index 000000000000..e747ac42fe3a
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright © 2006 Eric Anholt
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _INTEL_DVO_H
+#define _INTEL_DVO_H
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+
+struct intel_dvo_device {
+ char *name;
+ int type;
+ /* DVOA/B/C output register */
+ u32 dvo_reg;
+ /* GPIO register used for i2c bus to control this device */
+ u32 gpio;
+ int slave_addr;
+ struct intel_i2c_chan *i2c_bus;
+
+ const struct intel_dvo_dev_ops *dev_ops;
+ void *dev_priv;
+
+ struct drm_display_mode *panel_fixed_mode;
+ bool panel_wants_dither;
+};
+
+struct intel_dvo_dev_ops {
+ /*
+ * Initialize the device at startup time.
+ * Returns NULL if the device does not exist.
+ */
+ bool (*init)(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus);
+
+ /*
+ * Called to allow the output a chance to create properties after the
+ * RandR objects have been created.
+ */
+ void (*create_resources)(struct intel_dvo_device *dvo);
+
+ /*
+ * Turn on/off output or set intermediate power levels if available.
+ *
+ * Unsupported intermediate modes drop to the lower power setting.
+ * If the mode is DPMSModeOff, the output must be disabled,
+ * as the DPLL may be disabled afterwards.
+ */
+ void (*dpms)(struct intel_dvo_device *dvo, int mode);
+
+ /*
+ * Saves the output's state for restoration on VT switch.
+ */
+ void (*save)(struct intel_dvo_device *dvo);
+
+ /*
+ * Restore's the output's state at VT switch.
+ */
+ void (*restore)(struct intel_dvo_device *dvo);
+
+ /*
+ * Callback for testing a video mode for a given output.
+ *
+ * This function should only check for cases where a mode can't
+ * be supported on the output specifically, and not represent
+ * generic CRTC limitations.
+ *
+ * \return MODE_OK if the mode is valid, or another MODE_* otherwise.
+ */
+ int (*mode_valid)(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode);
+
+ /*
+ * Callback to adjust the mode to be set in the CRTC.
+ *
+ * This allows an output to adjust the clock or even the entire set of
+ * timings, which is used for panels with fixed timings or for
+ * buses with clock limitations.
+ */
+ bool (*mode_fixup)(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+
+ /*
+ * Callback for preparing mode changes on an output
+ */
+ void (*prepare)(struct intel_dvo_device *dvo);
+
+ /*
+ * Callback for committing mode changes on an output
+ */
+ void (*commit)(struct intel_dvo_device *dvo);
+
+ /*
+ * Callback for setting up a video mode after fixups have been made.
+ *
+ * This is only called while the output is disabled. The dpms callback
+ * must be all that's necessary for the output, to turn the output on
+ * after this function is called.
+ */
+ void (*mode_set)(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+
+ /*
+ * Probe for a connected output, and return detect_status.
+ */
+ enum drm_connector_status (*detect)(struct intel_dvo_device *dvo);
+
+ /**
+ * Query the device for the modes it provides.
+ *
+ * This function may also update MonInfo, mm_width, and mm_height.
+ *
+ * \return singly-linked list of modes or NULL if no modes found.
+ */
+ struct drm_display_mode *(*get_modes)(struct intel_dvo_device *dvo);
+
+ /**
+ * Clean up driver-specific bits of the output
+ */
+ void (*destroy) (struct intel_dvo_device *dvo);
+
+ /**
+ * Debugging hook to dump device registers to log file
+ */
+ void (*dump_regs)(struct intel_dvo_device *dvo);
+};
+
+extern struct intel_dvo_dev_ops sil164_ops;
+extern struct intel_dvo_dev_ops ch7xxx_ops;
+extern struct intel_dvo_dev_ops ivch_ops;
+extern struct intel_dvo_dev_ops tfp410_ops;
+extern struct intel_dvo_dev_ops ch7017_ops;
+
+#endif /* _INTEL_DVO_H */
diff --git a/drivers/gpu/drm/i915/dvo_ch7017.c b/drivers/gpu/drm/i915/dvo_ch7017.c
new file mode 100644
index 000000000000..03d4b4973b02
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo_ch7017.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+#include "dvo.h"
+
+#define CH7017_TV_DISPLAY_MODE 0x00
+#define CH7017_FLICKER_FILTER 0x01
+#define CH7017_VIDEO_BANDWIDTH 0x02
+#define CH7017_TEXT_ENHANCEMENT 0x03
+#define CH7017_START_ACTIVE_VIDEO 0x04
+#define CH7017_HORIZONTAL_POSITION 0x05
+#define CH7017_VERTICAL_POSITION 0x06
+#define CH7017_BLACK_LEVEL 0x07
+#define CH7017_CONTRAST_ENHANCEMENT 0x08
+#define CH7017_TV_PLL 0x09
+#define CH7017_TV_PLL_M 0x0a
+#define CH7017_TV_PLL_N 0x0b
+#define CH7017_SUB_CARRIER_0 0x0c
+#define CH7017_CIV_CONTROL 0x10
+#define CH7017_CIV_0 0x11
+#define CH7017_CHROMA_BOOST 0x14
+#define CH7017_CLOCK_MODE 0x1c
+#define CH7017_INPUT_CLOCK 0x1d
+#define CH7017_GPIO_CONTROL 0x1e
+#define CH7017_INPUT_DATA_FORMAT 0x1f
+#define CH7017_CONNECTION_DETECT 0x20
+#define CH7017_DAC_CONTROL 0x21
+#define CH7017_BUFFERED_CLOCK_OUTPUT 0x22
+#define CH7017_DEFEAT_VSYNC 0x47
+#define CH7017_TEST_PATTERN 0x48
+
+#define CH7017_POWER_MANAGEMENT 0x49
+/** Enables the TV output path. */
+#define CH7017_TV_EN (1 << 0)
+#define CH7017_DAC0_POWER_DOWN (1 << 1)
+#define CH7017_DAC1_POWER_DOWN (1 << 2)
+#define CH7017_DAC2_POWER_DOWN (1 << 3)
+#define CH7017_DAC3_POWER_DOWN (1 << 4)
+/** Powers down the TV out block, and DAC0-3 */
+#define CH7017_TV_POWER_DOWN_EN (1 << 5)
+
+#define CH7017_VERSION_ID 0x4a
+
+#define CH7017_DEVICE_ID 0x4b
+#define CH7017_DEVICE_ID_VALUE 0x1b
+#define CH7018_DEVICE_ID_VALUE 0x1a
+#define CH7019_DEVICE_ID_VALUE 0x19
+
+#define CH7017_XCLK_D2_ADJUST 0x53
+#define CH7017_UP_SCALER_COEFF_0 0x55
+#define CH7017_UP_SCALER_COEFF_1 0x56
+#define CH7017_UP_SCALER_COEFF_2 0x57
+#define CH7017_UP_SCALER_COEFF_3 0x58
+#define CH7017_UP_SCALER_COEFF_4 0x59
+#define CH7017_UP_SCALER_VERTICAL_INC_0 0x5a
+#define CH7017_UP_SCALER_VERTICAL_INC_1 0x5b
+#define CH7017_GPIO_INVERT 0x5c
+#define CH7017_UP_SCALER_HORIZONTAL_INC_0 0x5d
+#define CH7017_UP_SCALER_HORIZONTAL_INC_1 0x5e
+
+#define CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT 0x5f
+/**< Low bits of horizontal active pixel input */
+
+#define CH7017_ACTIVE_INPUT_LINE_OUTPUT 0x60
+/** High bits of horizontal active pixel input */
+#define CH7017_LVDS_HAP_INPUT_MASK (0x7 << 0)
+/** High bits of vertical active line output */
+#define CH7017_LVDS_VAL_HIGH_MASK (0x7 << 3)
+
+#define CH7017_VERTICAL_ACTIVE_LINE_OUTPUT 0x61
+/**< Low bits of vertical active line output */
+
+#define CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT 0x62
+/**< Low bits of horizontal active pixel output */
+
+#define CH7017_LVDS_POWER_DOWN 0x63
+/** High bits of horizontal active pixel output */
+#define CH7017_LVDS_HAP_HIGH_MASK (0x7 << 0)
+/** Enables the LVDS power down state transition */
+#define CH7017_LVDS_POWER_DOWN_EN (1 << 6)
+/** Enables the LVDS upscaler */
+#define CH7017_LVDS_UPSCALER_EN (1 << 7)
+#define CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED 0x08
+
+#define CH7017_LVDS_ENCODING 0x64
+#define CH7017_LVDS_DITHER_2D (1 << 2)
+#define CH7017_LVDS_DITHER_DIS (1 << 3)
+#define CH7017_LVDS_DUAL_CHANNEL_EN (1 << 4)
+#define CH7017_LVDS_24_BIT (1 << 5)
+
+#define CH7017_LVDS_ENCODING_2 0x65
+
+#define CH7017_LVDS_PLL_CONTROL 0x66
+/** Enables the LVDS panel output path */
+#define CH7017_LVDS_PANEN (1 << 0)
+/** Enables the LVDS panel backlight */
+#define CH7017_LVDS_BKLEN (1 << 3)
+
+#define CH7017_POWER_SEQUENCING_T1 0x67
+#define CH7017_POWER_SEQUENCING_T2 0x68
+#define CH7017_POWER_SEQUENCING_T3 0x69
+#define CH7017_POWER_SEQUENCING_T4 0x6a
+#define CH7017_POWER_SEQUENCING_T5 0x6b
+#define CH7017_GPIO_DRIVER_TYPE 0x6c
+#define CH7017_GPIO_DATA 0x6d
+#define CH7017_GPIO_DIRECTION_CONTROL 0x6e
+
+#define CH7017_LVDS_PLL_FEEDBACK_DIV 0x71
+# define CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT 4
+# define CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT 0
+# define CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED 0x80
+
+#define CH7017_LVDS_PLL_VCO_CONTROL 0x72
+# define CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED 0x80
+# define CH7017_LVDS_PLL_VCO_SHIFT 4
+# define CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT 0
+
+#define CH7017_OUTPUTS_ENABLE 0x73
+# define CH7017_CHARGE_PUMP_LOW 0x0
+# define CH7017_CHARGE_PUMP_HIGH 0x3
+# define CH7017_LVDS_CHANNEL_A (1 << 3)
+# define CH7017_LVDS_CHANNEL_B (1 << 4)
+# define CH7017_TV_DAC_A (1 << 5)
+# define CH7017_TV_DAC_B (1 << 6)
+# define CH7017_DDC_SELECT_DC2 (1 << 7)
+
+#define CH7017_LVDS_OUTPUT_AMPLITUDE 0x74
+#define CH7017_LVDS_PLL_EMI_REDUCTION 0x75
+#define CH7017_LVDS_POWER_DOWN_FLICKER 0x76
+
+#define CH7017_LVDS_CONTROL_2 0x78
+# define CH7017_LOOP_FILTER_SHIFT 5
+# define CH7017_PHASE_DETECTOR_SHIFT 0
+
+#define CH7017_BANG_LIMIT_CONTROL 0x7f
+
+struct ch7017_priv {
+ uint8_t save_hapi;
+ uint8_t save_vali;
+ uint8_t save_valo;
+ uint8_t save_ailo;
+ uint8_t save_lvds_pll_vco;
+ uint8_t save_feedback_div;
+ uint8_t save_lvds_control_2;
+ uint8_t save_outputs_enable;
+ uint8_t save_lvds_power_down;
+ uint8_t save_power_management;
+};
+
+static void ch7017_dump_regs(struct intel_dvo_device *dvo);
+static void ch7017_dpms(struct intel_dvo_device *dvo, int mode);
+
+static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val)
+{
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[2];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ *val= in_buf[0];
+ return true;
+ };
+
+ return false;
+}
+
+static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val)
+{
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ uint8_t out_buf[2];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = val;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ return false;
+}
+
+/** Probes for a CH7017 on the given bus and slave address. */
+static bool ch7017_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ struct ch7017_priv *priv;
+ uint8_t val;
+
+ priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = priv;
+
+ if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val))
+ goto fail;
+
+ if (val != CH7017_DEVICE_ID_VALUE &&
+ val != CH7018_DEVICE_ID_VALUE &&
+ val != CH7019_DEVICE_ID_VALUE) {
+ DRM_DEBUG("ch701x not detected, got %d: from %s Slave %d.\n",
+ val, i2cbus->adapter.name,i2cbus->slave_addr);
+ goto fail;
+ }
+
+ return true;
+fail:
+ kfree(priv);
+ return false;
+}
+
+static enum drm_connector_status ch7017_detect(struct intel_dvo_device *dvo)
+{
+ return connector_status_unknown;
+}
+
+static enum drm_mode_status ch7017_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock > 160000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static void ch7017_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ uint8_t lvds_pll_feedback_div, lvds_pll_vco_control;
+ uint8_t outputs_enable, lvds_control_2, lvds_power_down;
+ uint8_t horizontal_active_pixel_input;
+ uint8_t horizontal_active_pixel_output, vertical_active_line_output;
+ uint8_t active_input_line_output;
+
+ DRM_DEBUG("Registers before mode setting\n");
+ ch7017_dump_regs(dvo);
+
+ /* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/
+ if (mode->clock < 100000) {
+ outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_LOW;
+ lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
+ (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
+ (13 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
+ lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
+ (2 << CH7017_LVDS_PLL_VCO_SHIFT) |
+ (3 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
+ lvds_control_2 = (1 << CH7017_LOOP_FILTER_SHIFT) |
+ (0 << CH7017_PHASE_DETECTOR_SHIFT);
+ } else {
+ outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH;
+ lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
+ (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
+ (3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
+ lvds_pll_feedback_div = 35;
+ lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) |
+ (0 << CH7017_PHASE_DETECTOR_SHIFT);
+ if (1) { /* XXX: dual channel panel detection. Assume yes for now. */
+ outputs_enable |= CH7017_LVDS_CHANNEL_B;
+ lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
+ (2 << CH7017_LVDS_PLL_VCO_SHIFT) |
+ (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
+ } else {
+ lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
+ (1 << CH7017_LVDS_PLL_VCO_SHIFT) |
+ (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
+ }
+ }
+
+ horizontal_active_pixel_input = mode->hdisplay & 0x00ff;
+
+ vertical_active_line_output = mode->vdisplay & 0x00ff;
+ horizontal_active_pixel_output = mode->hdisplay & 0x00ff;
+
+ active_input_line_output = ((mode->hdisplay & 0x0700) >> 8) |
+ (((mode->vdisplay & 0x0700) >> 8) << 3);
+
+ lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED |
+ (mode->hdisplay & 0x0700) >> 8;
+
+ ch7017_dpms(dvo, DRM_MODE_DPMS_OFF);
+ ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT,
+ horizontal_active_pixel_input);
+ ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT,
+ horizontal_active_pixel_output);
+ ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT,
+ vertical_active_line_output);
+ ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT,
+ active_input_line_output);
+ ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, lvds_pll_vco_control);
+ ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, lvds_pll_feedback_div);
+ ch7017_write(dvo, CH7017_LVDS_CONTROL_2, lvds_control_2);
+ ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, outputs_enable);
+
+ /* Turn the LVDS back on with new settings. */
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down);
+
+ DRM_DEBUG("Registers after mode setting\n");
+ ch7017_dump_regs(dvo);
+}
+
+/* set the CH7017 power state */
+static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ uint8_t val;
+
+ ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val);
+
+ /* Turn off TV/VGA, and never turn it on since we don't support it. */
+ ch7017_write(dvo, CH7017_POWER_MANAGEMENT,
+ CH7017_DAC0_POWER_DOWN |
+ CH7017_DAC1_POWER_DOWN |
+ CH7017_DAC2_POWER_DOWN |
+ CH7017_DAC3_POWER_DOWN |
+ CH7017_TV_POWER_DOWN_EN);
+
+ if (mode == DRM_MODE_DPMS_ON) {
+ /* Turn on the LVDS */
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
+ val & ~CH7017_LVDS_POWER_DOWN_EN);
+ } else {
+ /* Turn off the LVDS */
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
+ val | CH7017_LVDS_POWER_DOWN_EN);
+ }
+
+ /* XXX: Should actually wait for update power status somehow */
+ udelay(20000);
+}
+
+static void ch7017_dump_regs(struct intel_dvo_device *dvo)
+{
+ uint8_t val;
+
+#define DUMP(reg) \
+do { \
+ ch7017_read(dvo, reg, &val); \
+ DRM_DEBUG(#reg ": %02x\n", val); \
+} while (0)
+
+ DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT);
+ DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT);
+ DUMP(CH7017_VERTICAL_ACTIVE_LINE_OUTPUT);
+ DUMP(CH7017_ACTIVE_INPUT_LINE_OUTPUT);
+ DUMP(CH7017_LVDS_PLL_VCO_CONTROL);
+ DUMP(CH7017_LVDS_PLL_FEEDBACK_DIV);
+ DUMP(CH7017_LVDS_CONTROL_2);
+ DUMP(CH7017_OUTPUTS_ENABLE);
+ DUMP(CH7017_LVDS_POWER_DOWN);
+}
+
+static void ch7017_save(struct intel_dvo_device *dvo)
+{
+ struct ch7017_priv *priv = dvo->dev_priv;
+
+ ch7017_read(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, &priv->save_hapi);
+ ch7017_read(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, &priv->save_valo);
+ ch7017_read(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, &priv->save_ailo);
+ ch7017_read(dvo, CH7017_LVDS_PLL_VCO_CONTROL, &priv->save_lvds_pll_vco);
+ ch7017_read(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, &priv->save_feedback_div);
+ ch7017_read(dvo, CH7017_LVDS_CONTROL_2, &priv->save_lvds_control_2);
+ ch7017_read(dvo, CH7017_OUTPUTS_ENABLE, &priv->save_outputs_enable);
+ ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &priv->save_lvds_power_down);
+ ch7017_read(dvo, CH7017_POWER_MANAGEMENT, &priv->save_power_management);
+}
+
+static void ch7017_restore(struct intel_dvo_device *dvo)
+{
+ struct ch7017_priv *priv = dvo->dev_priv;
+
+ /* Power down before changing mode */
+ ch7017_dpms(dvo, DRM_MODE_DPMS_OFF);
+
+ ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, priv->save_hapi);
+ ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, priv->save_valo);
+ ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, priv->save_ailo);
+ ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, priv->save_lvds_pll_vco);
+ ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, priv->save_feedback_div);
+ ch7017_write(dvo, CH7017_LVDS_CONTROL_2, priv->save_lvds_control_2);
+ ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, priv->save_outputs_enable);
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, priv->save_lvds_power_down);
+ ch7017_write(dvo, CH7017_POWER_MANAGEMENT, priv->save_power_management);
+}
+
+static void ch7017_destroy(struct intel_dvo_device *dvo)
+{
+ struct ch7017_priv *priv = dvo->dev_priv;
+
+ if (priv) {
+ kfree(priv);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops ch7017_ops = {
+ .init = ch7017_init,
+ .detect = ch7017_detect,
+ .mode_valid = ch7017_mode_valid,
+ .mode_set = ch7017_mode_set,
+ .dpms = ch7017_dpms,
+ .dump_regs = ch7017_dump_regs,
+ .save = ch7017_save,
+ .restore = ch7017_restore,
+ .destroy = ch7017_destroy,
+};
diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c
new file mode 100644
index 000000000000..d2fd95dbd034
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c
@@ -0,0 +1,368 @@
+/**************************************************************************
+
+Copyright © 2006 Dave Airlie
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+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 NON-INFRINGEMENT.
+IN NO EVENT SHALL THE AUTHOR 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 "dvo.h"
+
+#define CH7xxx_REG_VID 0x4a
+#define CH7xxx_REG_DID 0x4b
+
+#define CH7011_VID 0x83 /* 7010 as well */
+#define CH7009A_VID 0x84
+#define CH7009B_VID 0x85
+#define CH7301_VID 0x95
+
+#define CH7xxx_VID 0x84
+#define CH7xxx_DID 0x17
+
+#define CH7xxx_NUM_REGS 0x4c
+
+#define CH7xxx_CM 0x1c
+#define CH7xxx_CM_XCM (1<<0)
+#define CH7xxx_CM_MCP (1<<2)
+#define CH7xxx_INPUT_CLOCK 0x1d
+#define CH7xxx_GPIO 0x1e
+#define CH7xxx_GPIO_HPIR (1<<3)
+#define CH7xxx_IDF 0x1f
+
+#define CH7xxx_IDF_HSP (1<<3)
+#define CH7xxx_IDF_VSP (1<<4)
+
+#define CH7xxx_CONNECTION_DETECT 0x20
+#define CH7xxx_CDET_DVI (1<<5)
+
+#define CH7301_DAC_CNTL 0x21
+#define CH7301_HOTPLUG 0x23
+#define CH7xxx_TCTL 0x31
+#define CH7xxx_TVCO 0x32
+#define CH7xxx_TPCP 0x33
+#define CH7xxx_TPD 0x34
+#define CH7xxx_TPVT 0x35
+#define CH7xxx_TLPF 0x36
+#define CH7xxx_TCT 0x37
+#define CH7301_TEST_PATTERN 0x48
+
+#define CH7xxx_PM 0x49
+#define CH7xxx_PM_FPD (1<<0)
+#define CH7301_PM_DACPD0 (1<<1)
+#define CH7301_PM_DACPD1 (1<<2)
+#define CH7301_PM_DACPD2 (1<<3)
+#define CH7xxx_PM_DVIL (1<<6)
+#define CH7xxx_PM_DVIP (1<<7)
+
+#define CH7301_SYNC_POLARITY 0x56
+#define CH7301_SYNC_RGB_YUV (1<<0)
+#define CH7301_SYNC_POL_DVI (1<<5)
+
+/** @file
+ * driver for the Chrontel 7xxx DVI chip over DVO.
+ */
+
+static struct ch7xxx_id_struct {
+ uint8_t vid;
+ char *name;
+} ch7xxx_ids[] = {
+ { CH7011_VID, "CH7011" },
+ { CH7009A_VID, "CH7009A" },
+ { CH7009B_VID, "CH7009B" },
+ { CH7301_VID, "CH7301" },
+};
+
+struct ch7xxx_reg_state {
+ uint8_t regs[CH7xxx_NUM_REGS];
+};
+
+struct ch7xxx_priv {
+ bool quiet;
+
+ struct ch7xxx_reg_state save_reg;
+ struct ch7xxx_reg_state mode_reg;
+ uint8_t save_TCTL, save_TPCP, save_TPD, save_TPVT;
+ uint8_t save_TLPF, save_TCT, save_PM, save_IDF;
+};
+
+static void ch7xxx_save(struct intel_dvo_device *dvo);
+
+static char *ch7xxx_get_id(uint8_t vid)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) {
+ if (ch7xxx_ids[i].vid == vid)
+ return ch7xxx_ids[i].name;
+ }
+
+ return NULL;
+}
+
+/** Reads an 8 bit register */
+static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+{
+ struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[2];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ *ch = in_buf[0];
+ return true;
+ };
+
+ if (!ch7xxx->quiet) {
+ DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+ return false;
+}
+
+/** Writes an 8 bit register */
+static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ uint8_t out_buf[2];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ if (!ch7xxx->quiet) {
+ DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+
+ return false;
+}
+
+static bool ch7xxx_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ /* this will detect the CH7xxx chip on the specified i2c bus */
+ struct ch7xxx_priv *ch7xxx;
+ uint8_t vendor, device;
+ char *name;
+
+ ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL);
+ if (ch7xxx == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = ch7xxx;
+ ch7xxx->quiet = true;
+
+ if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor))
+ goto out;
+
+ name = ch7xxx_get_id(vendor);
+ if (!name) {
+ DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
+ vendor, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+
+
+ if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
+ goto out;
+
+ if (device != CH7xxx_DID) {
+ DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
+ vendor, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+
+ ch7xxx->quiet = false;
+ DRM_DEBUG("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
+ name, vendor, device);
+ return true;
+out:
+ kfree(ch7xxx);
+ return false;
+}
+
+static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo)
+{
+ uint8_t cdet, orig_pm, pm;
+
+ ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm);
+
+ pm = orig_pm;
+ pm &= ~CH7xxx_PM_FPD;
+ pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;
+
+ ch7xxx_writeb(dvo, CH7xxx_PM, pm);
+
+ ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet);
+
+ ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm);
+
+ if (cdet & CH7xxx_CDET_DVI)
+ return connector_status_connected;
+ return connector_status_disconnected;
+}
+
+static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock > 165000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ uint8_t tvco, tpcp, tpd, tlpf, idf;
+
+ if (mode->clock <= 65000) {
+ tvco = 0x23;
+ tpcp = 0x08;
+ tpd = 0x16;
+ tlpf = 0x60;
+ } else {
+ tvco = 0x2d;
+ tpcp = 0x06;
+ tpd = 0x26;
+ tlpf = 0xa0;
+ }
+
+ ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00);
+ ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco);
+ ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp);
+ ch7xxx_writeb(dvo, CH7xxx_TPD, tpd);
+ ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30);
+ ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf);
+ ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00);
+
+ ch7xxx_readb(dvo, CH7xxx_IDF, &idf);
+
+ idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ idf |= CH7xxx_IDF_HSP;
+
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ idf |= CH7xxx_IDF_HSP;
+
+ ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
+}
+
+/* set the CH7xxx power state */
+static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ if (mode == DRM_MODE_DPMS_ON)
+ ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
+ else
+ ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
+}
+
+static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
+{
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+ int i;
+
+ for (i = 0; i < CH7xxx_NUM_REGS; i++) {
+ if ((i % 8) == 0 )
+ DRM_DEBUG("\n %02X: ", i);
+ DRM_DEBUG("%02X ", ch7xxx->mode_reg.regs[i]);
+ }
+}
+
+static void ch7xxx_save(struct intel_dvo_device *dvo)
+{
+ struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
+
+ ch7xxx_readb(dvo, CH7xxx_TCTL, &ch7xxx->save_TCTL);
+ ch7xxx_readb(dvo, CH7xxx_TPCP, &ch7xxx->save_TPCP);
+ ch7xxx_readb(dvo, CH7xxx_TPD, &ch7xxx->save_TPD);
+ ch7xxx_readb(dvo, CH7xxx_TPVT, &ch7xxx->save_TPVT);
+ ch7xxx_readb(dvo, CH7xxx_TLPF, &ch7xxx->save_TLPF);
+ ch7xxx_readb(dvo, CH7xxx_PM, &ch7xxx->save_PM);
+ ch7xxx_readb(dvo, CH7xxx_IDF, &ch7xxx->save_IDF);
+}
+
+static void ch7xxx_restore(struct intel_dvo_device *dvo)
+{
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+
+ ch7xxx_writeb(dvo, CH7xxx_TCTL, ch7xxx->save_TCTL);
+ ch7xxx_writeb(dvo, CH7xxx_TPCP, ch7xxx->save_TPCP);
+ ch7xxx_writeb(dvo, CH7xxx_TPD, ch7xxx->save_TPD);
+ ch7xxx_writeb(dvo, CH7xxx_TPVT, ch7xxx->save_TPVT);
+ ch7xxx_writeb(dvo, CH7xxx_TLPF, ch7xxx->save_TLPF);
+ ch7xxx_writeb(dvo, CH7xxx_IDF, ch7xxx->save_IDF);
+ ch7xxx_writeb(dvo, CH7xxx_PM, ch7xxx->save_PM);
+}
+
+static void ch7xxx_destroy(struct intel_dvo_device *dvo)
+{
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+
+ if (ch7xxx) {
+ kfree(ch7xxx);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops ch7xxx_ops = {
+ .init = ch7xxx_init,
+ .detect = ch7xxx_detect,
+ .mode_valid = ch7xxx_mode_valid,
+ .mode_set = ch7xxx_mode_set,
+ .dpms = ch7xxx_dpms,
+ .dump_regs = ch7xxx_dump_regs,
+ .save = ch7xxx_save,
+ .restore = ch7xxx_restore,
+ .destroy = ch7xxx_destroy,
+};
diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c
new file mode 100644
index 000000000000..0c8d375e8e37
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo_ivch.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+#include "dvo.h"
+
+/*
+ * register definitions for the i82807aa.
+ *
+ * Documentation on this chipset can be found in datasheet #29069001 at
+ * intel.com.
+ */
+
+/*
+ * VCH Revision & GMBus Base Addr
+ */
+#define VR00 0x00
+# define VR00_BASE_ADDRESS_MASK 0x007f
+
+/*
+ * Functionality Enable
+ */
+#define VR01 0x01
+
+/*
+ * Enable the panel fitter
+ */
+# define VR01_PANEL_FIT_ENABLE (1 << 3)
+/*
+ * Enables the LCD display.
+ *
+ * This must not be set while VR01_DVO_BYPASS_ENABLE is set.
+ */
+# define VR01_LCD_ENABLE (1 << 2)
+/** Enables the DVO repeater. */
+# define VR01_DVO_BYPASS_ENABLE (1 << 1)
+/** Enables the DVO clock */
+# define VR01_DVO_ENABLE (1 << 0)
+
+/*
+ * LCD Interface Format
+ */
+#define VR10 0x10
+/** Enables LVDS output instead of CMOS */
+# define VR10_LVDS_ENABLE (1 << 4)
+/** Enables 18-bit LVDS output. */
+# define VR10_INTERFACE_1X18 (0 << 2)
+/** Enables 24-bit LVDS or CMOS output */
+# define VR10_INTERFACE_1X24 (1 << 2)
+/** Enables 2x18-bit LVDS or CMOS output. */
+# define VR10_INTERFACE_2X18 (2 << 2)
+/** Enables 2x24-bit LVDS output */
+# define VR10_INTERFACE_2X24 (3 << 2)
+
+/*
+ * VR20 LCD Horizontal Display Size
+ */
+#define VR20 0x20
+
+/*
+ * LCD Vertical Display Size
+ */
+#define VR21 0x20
+
+/*
+ * Panel power down status
+ */
+#define VR30 0x30
+/** Read only bit indicating that the panel is not in a safe poweroff state. */
+# define VR30_PANEL_ON (1 << 15)
+
+#define VR40 0x40
+# define VR40_STALL_ENABLE (1 << 13)
+# define VR40_VERTICAL_INTERP_ENABLE (1 << 12)
+# define VR40_ENHANCED_PANEL_FITTING (1 << 11)
+# define VR40_HORIZONTAL_INTERP_ENABLE (1 << 10)
+# define VR40_AUTO_RATIO_ENABLE (1 << 9)
+# define VR40_CLOCK_GATING_ENABLE (1 << 8)
+
+/*
+ * Panel Fitting Vertical Ratio
+ * (((image_height - 1) << 16) / ((panel_height - 1))) >> 2
+ */
+#define VR41 0x41
+
+/*
+ * Panel Fitting Horizontal Ratio
+ * (((image_width - 1) << 16) / ((panel_width - 1))) >> 2
+ */
+#define VR42 0x42
+
+/*
+ * Horizontal Image Size
+ */
+#define VR43 0x43
+
+/* VR80 GPIO 0
+ */
+#define VR80 0x80
+#define VR81 0x81
+#define VR82 0x82
+#define VR83 0x83
+#define VR84 0x84
+#define VR85 0x85
+#define VR86 0x86
+#define VR87 0x87
+
+/* VR88 GPIO 8
+ */
+#define VR88 0x88
+
+/* Graphics BIOS scratch 0
+ */
+#define VR8E 0x8E
+# define VR8E_PANEL_TYPE_MASK (0xf << 0)
+# define VR8E_PANEL_INTERFACE_CMOS (0 << 4)
+# define VR8E_PANEL_INTERFACE_LVDS (1 << 4)
+# define VR8E_FORCE_DEFAULT_PANEL (1 << 5)
+
+/* Graphics BIOS scratch 1
+ */
+#define VR8F 0x8F
+# define VR8F_VCH_PRESENT (1 << 0)
+# define VR8F_DISPLAY_CONN (1 << 1)
+# define VR8F_POWER_MASK (0x3c)
+# define VR8F_POWER_POS (2)
+
+
+struct ivch_priv {
+ bool quiet;
+
+ uint16_t width, height;
+
+ uint16_t save_VR01;
+ uint16_t save_VR40;
+};
+
+
+static void ivch_dump_regs(struct intel_dvo_device *dvo);
+
+/**
+ * Reads a register on the ivch.
+ *
+ * Each of the 256 registers are 16 bits long.
+ */
+static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[1];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 0,
+ },
+ {
+ .addr = 0,
+ .flags = I2C_M_NOSTART,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD | I2C_M_NOSTART,
+ .len = 2,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 3) == 3) {
+ *data = (in_buf[1] << 8) | in_buf[0];
+ return true;
+ };
+
+ if (!priv->quiet) {
+ DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+ return false;
+}
+
+/** Writes a 16-bit register on the ivch */
+static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[3];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 3,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = data & 0xff;
+ out_buf[2] = data >> 8;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ if (!priv->quiet) {
+ DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+
+ return false;
+}
+
+/** Probes the given bus and slave address for an ivch */
+static bool ivch_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ struct ivch_priv *priv;
+ uint16_t temp;
+
+ priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = priv;
+ priv->quiet = true;
+
+ if (!ivch_read(dvo, VR00, &temp))
+ goto out;
+ priv->quiet = false;
+
+ /* Since the identification bits are probably zeroes, which doesn't seem
+ * very unique, check that the value in the base address field matches
+ * the address it's responding on.
+ */
+ if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) {
+ DRM_DEBUG("ivch detect failed due to address mismatch "
+ "(%d vs %d)\n",
+ (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
+ goto out;
+ }
+
+ ivch_read(dvo, VR20, &priv->width);
+ ivch_read(dvo, VR21, &priv->height);
+
+ return true;
+
+out:
+ kfree(priv);
+ return false;
+}
+
+static enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo)
+{
+ return connector_status_connected;
+}
+
+static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock > 112000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+/** Sets the power state of the panel connected to the ivch */
+static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ int i;
+ uint16_t vr01, vr30, backlight;
+
+ /* Set the new power state of the panel. */
+ if (!ivch_read(dvo, VR01, &vr01))
+ return;
+
+ if (mode == DRM_MODE_DPMS_ON)
+ backlight = 1;
+ else
+ backlight = 0;
+ ivch_write(dvo, VR80, backlight);
+
+ if (mode == DRM_MODE_DPMS_ON)
+ vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
+ else
+ vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
+
+ ivch_write(dvo, VR01, vr01);
+
+ /* Wait for the panel to make its state transition */
+ for (i = 0; i < 100; i++) {
+ if (!ivch_read(dvo, VR30, &vr30))
+ break;
+
+ if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DRM_MODE_DPMS_ON))
+ break;
+ udelay(1000);
+ }
+ /* wait some more; vch may fail to resync sometimes without this */
+ udelay(16 * 1000);
+}
+
+static void ivch_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ uint16_t vr40 = 0;
+ uint16_t vr01;
+
+ vr01 = 0;
+ vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
+ VR40_HORIZONTAL_INTERP_ENABLE);
+
+ if (mode->hdisplay != adjusted_mode->hdisplay ||
+ mode->vdisplay != adjusted_mode->vdisplay) {
+ uint16_t x_ratio, y_ratio;
+
+ vr01 |= VR01_PANEL_FIT_ENABLE;
+ vr40 |= VR40_CLOCK_GATING_ENABLE;
+ x_ratio = (((mode->hdisplay - 1) << 16) /
+ (adjusted_mode->hdisplay - 1)) >> 2;
+ y_ratio = (((mode->vdisplay - 1) << 16) /
+ (adjusted_mode->vdisplay - 1)) >> 2;
+ ivch_write (dvo, VR42, x_ratio);
+ ivch_write (dvo, VR41, y_ratio);
+ } else {
+ vr01 &= ~VR01_PANEL_FIT_ENABLE;
+ vr40 &= ~VR40_CLOCK_GATING_ENABLE;
+ }
+ vr40 &= ~VR40_AUTO_RATIO_ENABLE;
+
+ ivch_write(dvo, VR01, vr01);
+ ivch_write(dvo, VR40, vr40);
+
+ ivch_dump_regs(dvo);
+}
+
+static void ivch_dump_regs(struct intel_dvo_device *dvo)
+{
+ uint16_t val;
+
+ ivch_read(dvo, VR00, &val);
+ DRM_DEBUG("VR00: 0x%04x\n", val);
+ ivch_read(dvo, VR01, &val);
+ DRM_DEBUG("VR01: 0x%04x\n", val);
+ ivch_read(dvo, VR30, &val);
+ DRM_DEBUG("VR30: 0x%04x\n", val);
+ ivch_read(dvo, VR40, &val);
+ DRM_DEBUG("VR40: 0x%04x\n", val);
+
+ /* GPIO registers */
+ ivch_read(dvo, VR80, &val);
+ DRM_DEBUG("VR80: 0x%04x\n", val);
+ ivch_read(dvo, VR81, &val);
+ DRM_DEBUG("VR81: 0x%04x\n", val);
+ ivch_read(dvo, VR82, &val);
+ DRM_DEBUG("VR82: 0x%04x\n", val);
+ ivch_read(dvo, VR83, &val);
+ DRM_DEBUG("VR83: 0x%04x\n", val);
+ ivch_read(dvo, VR84, &val);
+ DRM_DEBUG("VR84: 0x%04x\n", val);
+ ivch_read(dvo, VR85, &val);
+ DRM_DEBUG("VR85: 0x%04x\n", val);
+ ivch_read(dvo, VR86, &val);
+ DRM_DEBUG("VR86: 0x%04x\n", val);
+ ivch_read(dvo, VR87, &val);
+ DRM_DEBUG("VR87: 0x%04x\n", val);
+ ivch_read(dvo, VR88, &val);
+ DRM_DEBUG("VR88: 0x%04x\n", val);
+
+ /* Scratch register 0 - AIM Panel type */
+ ivch_read(dvo, VR8E, &val);
+ DRM_DEBUG("VR8E: 0x%04x\n", val);
+
+ /* Scratch register 1 - Status register */
+ ivch_read(dvo, VR8F, &val);
+ DRM_DEBUG("VR8F: 0x%04x\n", val);
+}
+
+static void ivch_save(struct intel_dvo_device *dvo)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+
+ ivch_read(dvo, VR01, &priv->save_VR01);
+ ivch_read(dvo, VR40, &priv->save_VR40);
+}
+
+static void ivch_restore(struct intel_dvo_device *dvo)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+
+ ivch_write(dvo, VR01, priv->save_VR01);
+ ivch_write(dvo, VR40, priv->save_VR40);
+}
+
+static void ivch_destroy(struct intel_dvo_device *dvo)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+
+ if (priv) {
+ kfree(priv);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops ivch_ops= {
+ .init = ivch_init,
+ .dpms = ivch_dpms,
+ .save = ivch_save,
+ .restore = ivch_restore,
+ .mode_valid = ivch_mode_valid,
+ .mode_set = ivch_mode_set,
+ .detect = ivch_detect,
+ .dump_regs = ivch_dump_regs,
+ .destroy = ivch_destroy,
+};
diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c
new file mode 100644
index 000000000000..033a4bb070b2
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo_sil164.c
@@ -0,0 +1,302 @@
+/**************************************************************************
+
+Copyright © 2006 Dave Airlie
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+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 NON-INFRINGEMENT.
+IN NO EVENT SHALL THE AUTHOR 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 "dvo.h"
+
+#define SIL164_VID 0x0001
+#define SIL164_DID 0x0006
+
+#define SIL164_VID_LO 0x00
+#define SIL164_VID_HI 0x01
+#define SIL164_DID_LO 0x02
+#define SIL164_DID_HI 0x03
+#define SIL164_REV 0x04
+#define SIL164_RSVD 0x05
+#define SIL164_FREQ_LO 0x06
+#define SIL164_FREQ_HI 0x07
+
+#define SIL164_REG8 0x08
+#define SIL164_8_VEN (1<<5)
+#define SIL164_8_HEN (1<<4)
+#define SIL164_8_DSEL (1<<3)
+#define SIL164_8_BSEL (1<<2)
+#define SIL164_8_EDGE (1<<1)
+#define SIL164_8_PD (1<<0)
+
+#define SIL164_REG9 0x09
+#define SIL164_9_VLOW (1<<7)
+#define SIL164_9_MSEL_MASK (0x7<<4)
+#define SIL164_9_TSEL (1<<3)
+#define SIL164_9_RSEN (1<<2)
+#define SIL164_9_HTPLG (1<<1)
+#define SIL164_9_MDI (1<<0)
+
+#define SIL164_REGC 0x0c
+
+struct sil164_save_rec {
+ uint8_t reg8;
+ uint8_t reg9;
+ uint8_t regc;
+};
+
+struct sil164_priv {
+ //I2CDevRec d;
+ bool quiet;
+ struct sil164_save_rec save_regs;
+ struct sil164_save_rec mode_regs;
+};
+
+#define SILPTR(d) ((SIL164Ptr)(d->DriverPrivate.ptr))
+
+static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+{
+ struct sil164_priv *sil = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[2];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ *ch = in_buf[0];
+ return true;
+ };
+
+ if (!sil->quiet) {
+ DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+ return false;
+}
+
+static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+ struct sil164_priv *sil= dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ uint8_t out_buf[2];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ if (!sil->quiet) {
+ DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+
+ return false;
+}
+
+/* Silicon Image 164 driver for chip on i2c bus */
+static bool sil164_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ /* this will detect the SIL164 chip on the specified i2c bus */
+ struct sil164_priv *sil;
+ unsigned char ch;
+
+ sil = kzalloc(sizeof(struct sil164_priv), GFP_KERNEL);
+ if (sil == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = sil;
+ sil->quiet = true;
+
+ if (!sil164_readb(dvo, SIL164_VID_LO, &ch))
+ goto out;
+
+ if (ch != (SIL164_VID & 0xff)) {
+ DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
+ ch, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+
+ if (!sil164_readb(dvo, SIL164_DID_LO, &ch))
+ goto out;
+
+ if (ch != (SIL164_DID & 0xff)) {
+ DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
+ ch, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+ sil->quiet = false;
+
+ DRM_DEBUG("init sil164 dvo controller successfully!\n");
+ return true;
+
+out:
+ kfree(sil);
+ return false;
+}
+
+static enum drm_connector_status sil164_detect(struct intel_dvo_device *dvo)
+{
+ uint8_t reg9;
+
+ sil164_readb(dvo, SIL164_REG9, &reg9);
+
+ if (reg9 & SIL164_9_HTPLG)
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+}
+
+static enum drm_mode_status sil164_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static void sil164_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* As long as the basics are set up, since we don't have clock
+ * dependencies in the mode setup, we can just leave the
+ * registers alone and everything will work fine.
+ */
+ /* recommended programming sequence from doc */
+ /*sil164_writeb(sil, 0x08, 0x30);
+ sil164_writeb(sil, 0x09, 0x00);
+ sil164_writeb(sil, 0x0a, 0x90);
+ sil164_writeb(sil, 0x0c, 0x89);
+ sil164_writeb(sil, 0x08, 0x31);*/
+ /* don't do much */
+ return;
+}
+
+/* set the SIL164 power state */
+static void sil164_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ int ret;
+ unsigned char ch;
+
+ ret = sil164_readb(dvo, SIL164_REG8, &ch);
+ if (ret == false)
+ return;
+
+ if (mode == DRM_MODE_DPMS_ON)
+ ch |= SIL164_8_PD;
+ else
+ ch &= ~SIL164_8_PD;
+
+ sil164_writeb(dvo, SIL164_REG8, ch);
+ return;
+}
+
+static void sil164_dump_regs(struct intel_dvo_device *dvo)
+{
+ uint8_t val;
+
+ sil164_readb(dvo, SIL164_FREQ_LO, &val);
+ DRM_DEBUG("SIL164_FREQ_LO: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_FREQ_HI, &val);
+ DRM_DEBUG("SIL164_FREQ_HI: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_REG8, &val);
+ DRM_DEBUG("SIL164_REG8: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_REG9, &val);
+ DRM_DEBUG("SIL164_REG9: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_REGC, &val);
+ DRM_DEBUG("SIL164_REGC: 0x%02x\n", val);
+}
+
+static void sil164_save(struct intel_dvo_device *dvo)
+{
+ struct sil164_priv *sil= dvo->dev_priv;
+
+ if (!sil164_readb(dvo, SIL164_REG8, &sil->save_regs.reg8))
+ return;
+
+ if (!sil164_readb(dvo, SIL164_REG9, &sil->save_regs.reg9))
+ return;
+
+ if (!sil164_readb(dvo, SIL164_REGC, &sil->save_regs.regc))
+ return;
+
+ return;
+}
+
+static void sil164_restore(struct intel_dvo_device *dvo)
+{
+ struct sil164_priv *sil = dvo->dev_priv;
+
+ /* Restore it powered down initially */
+ sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8 & ~0x1);
+
+ sil164_writeb(dvo, SIL164_REG9, sil->save_regs.reg9);
+ sil164_writeb(dvo, SIL164_REGC, sil->save_regs.regc);
+ sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8);
+}
+
+static void sil164_destroy(struct intel_dvo_device *dvo)
+{
+ struct sil164_priv *sil = dvo->dev_priv;
+
+ if (sil) {
+ kfree(sil);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops sil164_ops = {
+ .init = sil164_init,
+ .detect = sil164_detect,
+ .mode_valid = sil164_mode_valid,
+ .mode_set = sil164_mode_set,
+ .dpms = sil164_dpms,
+ .dump_regs = sil164_dump_regs,
+ .save = sil164_save,
+ .restore = sil164_restore,
+ .destroy = sil164_destroy,
+};
diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c
new file mode 100644
index 000000000000..207fda806ebf
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo_tfp410.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright © 2007 Dave Mueller
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Dave Mueller <dave.mueller@gmx.ch>
+ *
+ */
+
+#include "dvo.h"
+
+/* register definitions according to the TFP410 data sheet */
+#define TFP410_VID 0x014C
+#define TFP410_DID 0x0410
+
+#define TFP410_VID_LO 0x00
+#define TFP410_VID_HI 0x01
+#define TFP410_DID_LO 0x02
+#define TFP410_DID_HI 0x03
+#define TFP410_REV 0x04
+
+#define TFP410_CTL_1 0x08
+#define TFP410_CTL_1_TDIS (1<<6)
+#define TFP410_CTL_1_VEN (1<<5)
+#define TFP410_CTL_1_HEN (1<<4)
+#define TFP410_CTL_1_DSEL (1<<3)
+#define TFP410_CTL_1_BSEL (1<<2)
+#define TFP410_CTL_1_EDGE (1<<1)
+#define TFP410_CTL_1_PD (1<<0)
+
+#define TFP410_CTL_2 0x09
+#define TFP410_CTL_2_VLOW (1<<7)
+#define TFP410_CTL_2_MSEL_MASK (0x7<<4)
+#define TFP410_CTL_2_MSEL (1<<4)
+#define TFP410_CTL_2_TSEL (1<<3)
+#define TFP410_CTL_2_RSEN (1<<2)
+#define TFP410_CTL_2_HTPLG (1<<1)
+#define TFP410_CTL_2_MDI (1<<0)
+
+#define TFP410_CTL_3 0x0A
+#define TFP410_CTL_3_DK_MASK (0x7<<5)
+#define TFP410_CTL_3_DK (1<<5)
+#define TFP410_CTL_3_DKEN (1<<4)
+#define TFP410_CTL_3_CTL_MASK (0x7<<1)
+#define TFP410_CTL_3_CTL (1<<1)
+
+#define TFP410_USERCFG 0x0B
+
+#define TFP410_DE_DLY 0x32
+
+#define TFP410_DE_CTL 0x33
+#define TFP410_DE_CTL_DEGEN (1<<6)
+#define TFP410_DE_CTL_VSPOL (1<<5)
+#define TFP410_DE_CTL_HSPOL (1<<4)
+#define TFP410_DE_CTL_DEDLY8 (1<<0)
+
+#define TFP410_DE_TOP 0x34
+
+#define TFP410_DE_CNT_LO 0x36
+#define TFP410_DE_CNT_HI 0x37
+
+#define TFP410_DE_LIN_LO 0x38
+#define TFP410_DE_LIN_HI 0x39
+
+#define TFP410_H_RES_LO 0x3A
+#define TFP410_H_RES_HI 0x3B
+
+#define TFP410_V_RES_LO 0x3C
+#define TFP410_V_RES_HI 0x3D
+
+struct tfp410_save_rec {
+ uint8_t ctl1;
+ uint8_t ctl2;
+};
+
+struct tfp410_priv {
+ bool quiet;
+
+ struct tfp410_save_rec saved_reg;
+ struct tfp410_save_rec mode_reg;
+};
+
+static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[2];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ *ch = in_buf[0];
+ return true;
+ };
+
+ if (!tfp->quiet) {
+ DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+ return false;
+}
+
+static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ uint8_t out_buf[2];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ if (!tfp->quiet) {
+ DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+
+ return false;
+}
+
+static int tfp410_getid(struct intel_dvo_device *dvo, int addr)
+{
+ uint8_t ch1, ch2;
+
+ if (tfp410_readb(dvo, addr+0, &ch1) &&
+ tfp410_readb(dvo, addr+1, &ch2))
+ return ((ch2 << 8) & 0xFF00) | (ch1 & 0x00FF);
+
+ return -1;
+}
+
+/* Ti TFP410 driver for chip on i2c bus */
+static bool tfp410_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ /* this will detect the tfp410 chip on the specified i2c bus */
+ struct tfp410_priv *tfp;
+ int id;
+
+ tfp = kzalloc(sizeof(struct tfp410_priv), GFP_KERNEL);
+ if (tfp == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = tfp;
+ tfp->quiet = true;
+
+ if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) {
+ DRM_DEBUG("tfp410 not detected got VID %X: from %s Slave %d.\n",
+ id, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+
+ if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) {
+ DRM_DEBUG("tfp410 not detected got DID %X: from %s Slave %d.\n",
+ id, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+ tfp->quiet = false;
+ return true;
+out:
+ kfree(tfp);
+ return false;
+}
+
+static enum drm_connector_status tfp410_detect(struct intel_dvo_device *dvo)
+{
+ enum drm_connector_status ret = connector_status_disconnected;
+ uint8_t ctl2;
+
+ if (tfp410_readb(dvo, TFP410_CTL_2, &ctl2)) {
+ if (ctl2 & TFP410_CTL_2_HTPLG)
+ ret = connector_status_connected;
+ else
+ ret = connector_status_disconnected;
+ }
+
+ return ret;
+}
+
+static enum drm_mode_status tfp410_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static void tfp410_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* As long as the basics are set up, since we don't have clock dependencies
+ * in the mode setup, we can just leave the registers alone and everything
+ * will work fine.
+ */
+ /* don't do much */
+ return;
+}
+
+/* set the tfp410 power state */
+static void tfp410_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ uint8_t ctl1;
+
+ if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
+ return;
+
+ if (mode == DRM_MODE_DPMS_ON)
+ ctl1 |= TFP410_CTL_1_PD;
+ else
+ ctl1 &= ~TFP410_CTL_1_PD;
+
+ tfp410_writeb(dvo, TFP410_CTL_1, ctl1);
+}
+
+static void tfp410_dump_regs(struct intel_dvo_device *dvo)
+{
+ uint8_t val, val2;
+
+ tfp410_readb(dvo, TFP410_REV, &val);
+ DRM_DEBUG("TFP410_REV: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_CTL_1, &val);
+ DRM_DEBUG("TFP410_CTL1: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_CTL_2, &val);
+ DRM_DEBUG("TFP410_CTL2: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_CTL_3, &val);
+ DRM_DEBUG("TFP410_CTL3: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_USERCFG, &val);
+ DRM_DEBUG("TFP410_USERCFG: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_DLY, &val);
+ DRM_DEBUG("TFP410_DE_DLY: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_CTL, &val);
+ DRM_DEBUG("TFP410_DE_CTL: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_TOP, &val);
+ DRM_DEBUG("TFP410_DE_TOP: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_CNT_LO, &val);
+ tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2);
+ DRM_DEBUG("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
+ tfp410_readb(dvo, TFP410_DE_LIN_LO, &val);
+ tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2);
+ DRM_DEBUG("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
+ tfp410_readb(dvo, TFP410_H_RES_LO, &val);
+ tfp410_readb(dvo, TFP410_H_RES_HI, &val2);
+ DRM_DEBUG("TFP410_H_RES: 0x%02X%02X\n", val2, val);
+ tfp410_readb(dvo, TFP410_V_RES_LO, &val);
+ tfp410_readb(dvo, TFP410_V_RES_HI, &val2);
+ DRM_DEBUG("TFP410_V_RES: 0x%02X%02X\n", val2, val);
+}
+
+static void tfp410_save(struct intel_dvo_device *dvo)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+
+ if (!tfp410_readb(dvo, TFP410_CTL_1, &tfp->saved_reg.ctl1))
+ return;
+
+ if (!tfp410_readb(dvo, TFP410_CTL_2, &tfp->saved_reg.ctl2))
+ return;
+}
+
+static void tfp410_restore(struct intel_dvo_device *dvo)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+
+ /* Restore it powered down initially */
+ tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1 & ~0x1);
+
+ tfp410_writeb(dvo, TFP410_CTL_2, tfp->saved_reg.ctl2);
+ tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1);
+}
+
+static void tfp410_destroy(struct intel_dvo_device *dvo)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+
+ if (tfp) {
+ kfree(tfp);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops tfp410_ops = {
+ .init = tfp410_init,
+ .detect = tfp410_detect,
+ .mode_valid = tfp410_mode_valid,
+ .mode_set = tfp410_mode_set,
+ .dpms = tfp410_dpms,
+ .dump_regs = tfp410_dump_regs,
+ .save = tfp410_save,
+ .restore = tfp410_restore,
+ .destroy = tfp410_destroy,
+};
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index afa8a12cd009..3d7082af5b72 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -28,6 +28,8 @@
#include "drmP.h"
#include "drm.h"
+#include "drm_crtc_helper.h"
+#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"
@@ -39,6 +41,7 @@
int i915_wait_ring(struct drm_device * dev, int n, const char *caller)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
drm_i915_ring_buffer_t *ring = &(dev_priv->ring);
u32 acthd_reg = IS_I965G(dev) ? ACTHD_I965 : ACTHD;
u32 last_acthd = I915_READ(acthd_reg);
@@ -55,8 +58,8 @@ int i915_wait_ring(struct drm_device * dev, int n, const char *caller)
if (ring->space >= n)
return 0;
- if (dev_priv->sarea_priv)
- dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
if (ring->head != last_head)
i = 0;
@@ -121,16 +124,28 @@ static void i915_free_hws(struct drm_device *dev)
void i915_kernel_lost_context(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv;
drm_i915_ring_buffer_t *ring = &(dev_priv->ring);
+ /*
+ * We should never lose context on the ring with modesetting
+ * as we don't expose it to userspace
+ */
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR;
ring->tail = I915_READ(PRB0_TAIL) & TAIL_ADDR;
ring->space = ring->head - (ring->tail + 8);
if (ring->space < 0)
ring->space += ring->Size;
- if (ring->head == ring->tail && dev_priv->sarea_priv)
- dev_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;
+ if (!dev->primary->master)
+ return;
+
+ master_priv = dev->primary->master->driver_priv;
+ if (ring->head == ring->tail && master_priv->sarea_priv)
+ master_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;
}
static int i915_dma_cleanup(struct drm_device * dev)
@@ -154,25 +169,13 @@ static int i915_dma_cleanup(struct drm_device * dev)
if (I915_NEED_GFX_HWS(dev))
i915_free_hws(dev);
- dev_priv->sarea = NULL;
- dev_priv->sarea_priv = NULL;
-
return 0;
}
static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
{
drm_i915_private_t *dev_priv = dev->dev_private;
-
- dev_priv->sarea = drm_getsarea(dev);
- if (!dev_priv->sarea) {
- DRM_ERROR("can not find sarea!\n");
- i915_dma_cleanup(dev);
- return -EINVAL;
- }
-
- dev_priv->sarea_priv = (drm_i915_sarea_t *)
- ((u8 *) dev_priv->sarea->handle + init->sarea_priv_offset);
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
if (init->ring_size != 0) {
if (dev_priv->ring.ring_obj != NULL) {
@@ -207,7 +210,8 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
dev_priv->back_offset = init->back_offset;
dev_priv->front_offset = init->front_offset;
dev_priv->current_page = 0;
- dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->pf_current_page = 0;
/* Allow hardware batchbuffers unless told otherwise.
*/
@@ -222,11 +226,6 @@ static int i915_dma_resume(struct drm_device * dev)
DRM_DEBUG("%s\n", __func__);
- if (!dev_priv->sarea) {
- DRM_ERROR("can not find sarea!\n");
- return -EINVAL;
- }
-
if (dev_priv->ring.map.handle == NULL) {
DRM_ERROR("can not ioremap virtual address for"
" ring buffer\n");
@@ -435,13 +434,14 @@ i915_emit_box(struct drm_device *dev,
static void i915_emit_breadcrumb(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
RING_LOCALS;
dev_priv->counter++;
if (dev_priv->counter > 0x7FFFFFFFUL)
dev_priv->counter = 0;
- if (dev_priv->sarea_priv)
- dev_priv->sarea_priv->last_enqueue = dev_priv->counter;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->last_enqueue = dev_priv->counter;
BEGIN_LP_RING(4);
OUT_RING(MI_STORE_DWORD_INDEX);
@@ -537,15 +537,17 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev,
static int i915_dispatch_flip(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv =
+ dev->primary->master->driver_priv;
RING_LOCALS;
- if (!dev_priv->sarea_priv)
+ if (!master_priv->sarea_priv)
return -EINVAL;
DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n",
__func__,
dev_priv->current_page,
- dev_priv->sarea_priv->pf_current_page);
+ master_priv->sarea_priv->pf_current_page);
i915_kernel_lost_context(dev);
@@ -572,7 +574,7 @@ static int i915_dispatch_flip(struct drm_device * dev)
OUT_RING(0);
ADVANCE_LP_RING();
- dev_priv->sarea_priv->last_enqueue = dev_priv->counter++;
+ master_priv->sarea_priv->last_enqueue = dev_priv->counter++;
BEGIN_LP_RING(4);
OUT_RING(MI_STORE_DWORD_INDEX);
@@ -581,7 +583,7 @@ static int i915_dispatch_flip(struct drm_device * dev)
OUT_RING(0);
ADVANCE_LP_RING();
- dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;
+ master_priv->sarea_priv->pf_current_page = dev_priv->current_page;
return 0;
}
@@ -611,8 +613,9 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
- dev_priv->sarea_priv;
+ master_priv->sarea_priv;
drm_i915_batchbuffer_t *batch = data;
int ret;
@@ -644,8 +647,9 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
- dev_priv->sarea_priv;
+ master_priv->sarea_priv;
drm_i915_cmdbuffer_t *cmdbuf = data;
int ret;
@@ -774,6 +778,11 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
return -EINVAL;
}
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ WARN(1, "tried to set status page when mode setting active\n");
+ return 0;
+ }
+
printk(KERN_DEBUG "set status page addr 0x%08x\n", (u32)hws->addr);
dev_priv->status_gfx_addr = hws->addr & (0x1ffff<<12);
@@ -802,6 +811,214 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
return 0;
}
+/**
+ * i915_probe_agp - get AGP bootup configuration
+ * @pdev: PCI device
+ * @aperture_size: returns AGP aperture configured size
+ * @preallocated_size: returns size of BIOS preallocated AGP space
+ *
+ * Since Intel integrated graphics are UMA, the BIOS has to set aside
+ * some RAM for the framebuffer at early boot. This code figures out
+ * how much was set aside so we can use it for our own purposes.
+ */
+static int i915_probe_agp(struct drm_device *dev, unsigned long *aperture_size,
+ unsigned long *preallocated_size)
+{
+ struct pci_dev *bridge_dev;
+ u16 tmp = 0;
+ unsigned long overhead;
+
+ bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0));
+ if (!bridge_dev) {
+ DRM_ERROR("bridge device not found\n");
+ return -1;
+ }
+
+ /* Get the fb aperture size and "stolen" memory amount. */
+ pci_read_config_word(bridge_dev, INTEL_GMCH_CTRL, &tmp);
+ pci_dev_put(bridge_dev);
+
+ *aperture_size = 1024 * 1024;
+ *preallocated_size = 1024 * 1024;
+
+ switch (dev->pdev->device) {
+ case PCI_DEVICE_ID_INTEL_82830_CGC:
+ case PCI_DEVICE_ID_INTEL_82845G_IG:
+ case PCI_DEVICE_ID_INTEL_82855GM_IG:
+ case PCI_DEVICE_ID_INTEL_82865_IG:
+ if ((tmp & INTEL_GMCH_MEM_MASK) == INTEL_GMCH_MEM_64M)
+ *aperture_size *= 64;
+ else
+ *aperture_size *= 128;
+ break;
+ default:
+ /* 9xx supports large sizes, just look at the length */
+ *aperture_size = pci_resource_len(dev->pdev, 2);
+ break;
+ }
+
+ /*
+ * Some of the preallocated space is taken by the GTT
+ * and popup. GTT is 1K per MB of aperture size, and popup is 4K.
+ */
+ if (IS_G4X(dev))
+ overhead = 4096;
+ else
+ overhead = (*aperture_size / 1024) + 4096;
+
+ switch (tmp & INTEL_855_GMCH_GMS_MASK) {
+ case INTEL_855_GMCH_GMS_STOLEN_1M:
+ break; /* 1M already */
+ case INTEL_855_GMCH_GMS_STOLEN_4M:
+ *preallocated_size *= 4;
+ break;
+ case INTEL_855_GMCH_GMS_STOLEN_8M:
+ *preallocated_size *= 8;
+ break;
+ case INTEL_855_GMCH_GMS_STOLEN_16M:
+ *preallocated_size *= 16;
+ break;
+ case INTEL_855_GMCH_GMS_STOLEN_32M:
+ *preallocated_size *= 32;
+ break;
+ case INTEL_915G_GMCH_GMS_STOLEN_48M:
+ *preallocated_size *= 48;
+ break;
+ case INTEL_915G_GMCH_GMS_STOLEN_64M:
+ *preallocated_size *= 64;
+ break;
+ case INTEL_855_GMCH_GMS_DISABLED:
+ DRM_ERROR("video memory is disabled\n");
+ return -1;
+ default:
+ DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n",
+ tmp & INTEL_855_GMCH_GMS_MASK);
+ return -1;
+ }
+ *preallocated_size -= overhead;
+
+ return 0;
+}
+
+static int i915_load_modeset_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned long agp_size, prealloc_size;
+ int fb_bar = IS_I9XX(dev) ? 2 : 0;
+ int ret = 0;
+
+ dev->devname = kstrdup(DRIVER_NAME, GFP_KERNEL);
+ if (!dev->devname) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ dev->mode_config.fb_base = drm_get_resource_start(dev, fb_bar) &
+ 0xff000000;
+
+ DRM_DEBUG("*** fb base 0x%08lx\n", dev->mode_config.fb_base);
+
+ if (IS_MOBILE(dev) || (IS_I9XX(dev) && !IS_I965G(dev) && !IS_G33(dev)))
+ dev_priv->cursor_needs_physical = true;
+ else
+ dev_priv->cursor_needs_physical = false;
+
+ ret = i915_probe_agp(dev, &agp_size, &prealloc_size);
+ if (ret)
+ goto kfree_devname;
+
+ /* Basic memrange allocator for stolen space (aka vram) */
+ drm_mm_init(&dev_priv->vram, 0, prealloc_size);
+
+ /* Let GEM Manage from end of prealloc space to end of aperture */
+ i915_gem_do_init(dev, prealloc_size, agp_size);
+
+ ret = i915_gem_init_ringbuffer(dev);
+ if (ret)
+ goto kfree_devname;
+
+ dev_priv->mm.gtt_mapping =
+ io_mapping_create_wc(dev->agp->base,
+ dev->agp->agp_info.aper_size * 1024*1024);
+
+ /* Allow hardware batchbuffers unless told otherwise.
+ */
+ dev_priv->allow_batchbuffer = 1;
+
+ ret = intel_init_bios(dev);
+ if (ret)
+ DRM_INFO("failed to find VBIOS tables\n");
+
+ ret = drm_irq_install(dev);
+ if (ret)
+ goto destroy_ringbuffer;
+
+ /* FIXME: re-add hotplug support */
+#if 0
+ ret = drm_hotplug_init(dev);
+ if (ret)
+ goto destroy_ringbuffer;
+#endif
+
+ /* Always safe in the mode setting case. */
+ /* FIXME: do pre/post-mode set stuff in core KMS code */
+ dev->vblank_disable_allowed = 1;
+
+ /*
+ * Initialize the hardware status page IRQ location.
+ */
+
+ I915_WRITE(INSTPM, (1 << 5) | (1 << 21));
+
+ intel_modeset_init(dev);
+
+ drm_helper_initial_config(dev, false);
+
+ return 0;
+
+destroy_ringbuffer:
+ i915_gem_cleanup_ringbuffer(dev);
+kfree_devname:
+ kfree(dev->devname);
+out:
+ return ret;
+}
+
+int i915_master_create(struct drm_device *dev, struct drm_master *master)
+{
+ struct drm_i915_master_private *master_priv;
+
+ master_priv = drm_calloc(1, sizeof(*master_priv), DRM_MEM_DRIVER);
+ if (!master_priv)
+ return -ENOMEM;
+
+ master->driver_priv = master_priv;
+ return 0;
+}
+
+void i915_master_destroy(struct drm_device *dev, struct drm_master *master)
+{
+ struct drm_i915_master_private *master_priv = master->driver_priv;
+
+ if (!master_priv)
+ return;
+
+ drm_free(master_priv, sizeof(*master_priv), DRM_MEM_DRIVER);
+
+ master->driver_priv = NULL;
+}
+
+/**
+ * i915_driver_load - setup chip and create an initial config
+ * @dev: DRM device
+ * @flags: startup flags
+ *
+ * The driver load routine has to do several things:
+ * - drive output discovery via intel_modeset_init()
+ * - initialize the memory manager
+ * - allocate initial config memory
+ * - setup the DRM framebuffer with the allocated memory
+ */
int i915_driver_load(struct drm_device *dev, unsigned long flags)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -829,6 +1046,11 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
size = drm_get_resource_len(dev, mmio_bar);
dev_priv->regs = ioremap(base, size);
+ if (!dev_priv->regs) {
+ DRM_ERROR("failed to map registers\n");
+ ret = -EIO;
+ goto free_priv;
+ }
#ifdef CONFIG_HIGHMEM64G
/* don't enable GEM on PAE - needs agp + set_memory_* interface fixes */
@@ -844,7 +1066,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (!I915_NEED_GFX_HWS(dev)) {
ret = i915_init_phys_hws(dev);
if (ret != 0)
- return ret;
+ goto out_rmmap;
}
/* On the 945G/GM, the chipset reports the MSI capability on the
@@ -864,6 +1086,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
intel_opregion_init(dev);
spin_lock_init(&dev_priv->user_irq_lock);
+ dev_priv->user_irq_refcount = 0;
ret = drm_vblank_init(dev, I915_NUM_PIPE);
@@ -872,6 +1095,20 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
return ret;
}
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ ret = i915_load_modeset_init(dev);
+ if (ret < 0) {
+ DRM_ERROR("failed to init modeset\n");
+ goto out_rmmap;
+ }
+ }
+
+ return 0;
+
+out_rmmap:
+ iounmap(dev_priv->regs);
+free_priv:
+ drm_free(dev_priv, sizeof(struct drm_i915_private), DRM_MEM_DRIVER);
return ret;
}
@@ -879,16 +1116,29 @@ int i915_driver_unload(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ io_mapping_free(dev_priv->mm.gtt_mapping);
+ drm_irq_uninstall(dev);
+ }
+
if (dev->pdev->msi_enabled)
pci_disable_msi(dev->pdev);
- i915_free_hws(dev);
-
if (dev_priv->regs != NULL)
iounmap(dev_priv->regs);
intel_opregion_free(dev);
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ intel_modeset_cleanup(dev);
+
+ mutex_lock(&dev->struct_mutex);
+ i915_gem_cleanup_ringbuffer(dev);
+ mutex_unlock(&dev->struct_mutex);
+ drm_mm_takedown(&dev_priv->vram);
+ i915_gem_lastclose(dev);
+ }
+
drm_free(dev->dev_private, sizeof(drm_i915_private_t),
DRM_MEM_DRIVER);
@@ -914,12 +1164,26 @@ int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv)
return 0;
}
+/**
+ * i915_driver_lastclose - clean up after all DRM clients have exited
+ * @dev: DRM device
+ *
+ * Take care of cleaning up after all DRM clients have exited. In the
+ * mode setting case, we want to restore the kernel's initial mode (just
+ * in case the last client left us in a bad state).
+ *
+ * Additionally, in the non-mode setting case, we'll tear down the AGP
+ * and DMA structures, since the kernel won't be using them, and clea
+ * up any GEM state.
+ */
void i915_driver_lastclose(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- if (!dev_priv)
+ if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) {
+ intelfb_restore();
return;
+ }
i915_gem_lastclose(dev);
@@ -932,7 +1196,8 @@ void i915_driver_lastclose(struct drm_device * dev)
void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- i915_mem_release(dev, file_priv, dev_priv->agp_heap);
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ i915_mem_release(dev, file_priv, dev_priv->agp_heap);
}
void i915_driver_postclose(struct drm_device *dev, struct drm_file *file_priv)
@@ -972,6 +1237,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
DRM_IOCTL_DEF(DRM_I915_GEM_PREAD, i915_gem_pread_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_PWRITE, i915_gem_pwrite_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_MMAP, i915_gem_mmap_ioctl, 0),
+ DRM_IOCTL_DEF(DRM_I915_GEM_MMAP_GTT, i915_gem_mmap_gtt_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_SET_DOMAIN, i915_gem_set_domain_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_SW_FINISH, i915_gem_sw_finish_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_SET_TILING, i915_gem_set_tiling, 0),
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index a80ead215282..f8b3df0926c0 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -33,11 +33,22 @@
#include "i915_drv.h"
#include "drm_pciids.h"
+#include <linux/console.h>
+
+static unsigned int i915_modeset = -1;
+module_param_named(modeset, i915_modeset, int, 0400);
+
+unsigned int i915_fbpercrtc = 0;
+module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400);
static struct pci_device_id pciidlist[] = {
i915_PCI_IDS
};
+#if defined(CONFIG_DRM_I915_KMS)
+MODULE_DEVICE_TABLE(pci, pciidlist);
+#endif
+
static int i915_suspend(struct drm_device *dev, pm_message_t state)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -81,6 +92,10 @@ static int i915_resume(struct drm_device *dev)
return 0;
}
+static struct vm_operations_struct i915_gem_vm_ops = {
+ .fault = i915_gem_fault,
+};
+
static struct drm_driver driver = {
/* don't use mtrr's here, the Xserver or user space app should
* deal with them for intel hardware.
@@ -107,17 +122,20 @@ static struct drm_driver driver = {
.reclaim_buffers = drm_core_reclaim_buffers,
.get_map_ofs = drm_core_get_map_ofs,
.get_reg_ofs = drm_core_get_reg_ofs,
+ .master_create = i915_master_create,
+ .master_destroy = i915_master_destroy,
.proc_init = i915_gem_proc_init,
.proc_cleanup = i915_gem_proc_cleanup,
.gem_init_object = i915_gem_init_object,
.gem_free_object = i915_gem_free_object,
+ .gem_vm_ops = &i915_gem_vm_ops,
.ioctls = i915_ioctls,
.fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
.ioctl = drm_ioctl,
- .mmap = drm_mmap,
+ .mmap = drm_gem_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
#ifdef CONFIG_COMPAT
@@ -141,6 +159,28 @@ static struct drm_driver driver = {
static int __init i915_init(void)
{
driver.num_ioctls = i915_max_ioctl;
+
+ /*
+ * If CONFIG_DRM_I915_KMS is set, default to KMS unless
+ * explicitly disabled with the module pararmeter.
+ *
+ * Otherwise, just follow the parameter (defaulting to off).
+ *
+ * Allow optional vga_text_mode_force boot option to override
+ * the default behavior.
+ */
+#if defined(CONFIG_DRM_I915_KMS)
+ if (i915_modeset != 0)
+ driver.driver_features |= DRIVER_MODESET;
+#endif
+ if (i915_modeset == 1)
+ driver.driver_features |= DRIVER_MODESET;
+
+#ifdef CONFIG_VGA_CONSOLE
+ if (vgacon_text_force() && i915_modeset == -1)
+ driver.driver_features &= ~DRIVER_MODESET;
+#endif
+
return drm_init(&driver);
}
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index b3cc4731aa7c..4756e5cd6b5e 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -31,6 +31,7 @@
#define _I915_DRV_H_
#include "i915_reg.h"
+#include "intel_bios.h"
#include <linux/io-mapping.h>
/* General customization:
@@ -103,15 +104,23 @@ struct intel_opregion {
int enabled;
};
+struct drm_i915_master_private {
+ drm_local_map_t *sarea;
+ struct _drm_i915_sarea *sarea_priv;
+};
+#define I915_FENCE_REG_NONE -1
+
+struct drm_i915_fence_reg {
+ struct drm_gem_object *obj;
+};
+
typedef struct drm_i915_private {
struct drm_device *dev;
int has_gem;
void __iomem *regs;
- drm_local_map_t *sarea;
- drm_i915_sarea_t *sarea_priv;
drm_i915_ring_buffer_t ring;
drm_dma_handle_t *status_page_dmah;
@@ -144,8 +153,30 @@ typedef struct drm_i915_private {
unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds;
int vblank_pipe;
+ bool cursor_needs_physical;
+
+ struct drm_mm vram;
+
+ int irq_enabled;
+
struct intel_opregion opregion;
+ /* LVDS info */
+ int backlight_duty_cycle; /* restore backlight to this value */
+ bool panel_wants_dither;
+ struct drm_display_mode *panel_fixed_mode;
+ struct drm_display_mode *vbt_mode; /* if any */
+
+ /* Feature bits from the VBIOS */
+ unsigned int int_tv_support:1;
+ unsigned int lvds_dither:1;
+ unsigned int lvds_vbt:1;
+ unsigned int int_crt_support:1;
+
+ struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
+ int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
+ int num_fence_regs; /* 8 on pre-965, 16 otherwise */
+
/* Register state */
u8 saveLBB;
u32 saveDSPACNTR;
@@ -364,6 +395,21 @@ struct drm_i915_gem_object {
* This is the same as gtt_space->start
*/
uint32_t gtt_offset;
+ /**
+ * Required alignment for the object
+ */
+ uint32_t gtt_alignment;
+ /**
+ * Fake offset for use by mmap(2)
+ */
+ uint64_t mmap_offset;
+
+ /**
+ * Fence register bits (if any) for this object. Will be set
+ * as needed when mapped into the GTT.
+ * Protected by dev->struct_mutex.
+ */
+ int fence_reg;
/** Boolean whether this object has a valid gtt offset. */
int gtt_bound;
@@ -376,6 +422,7 @@ struct drm_i915_gem_object {
/** Current tiling mode for the object. */
uint32_t tiling_mode;
+ uint32_t stride;
/** AGP mapping type (AGP_USER_MEMORY or AGP_USER_CACHED_MEMORY */
uint32_t agp_type;
@@ -385,6 +432,10 @@ struct drm_i915_gem_object {
* flags which individual pages are valid.
*/
uint8_t *page_cpu_valid;
+
+ /** User space pin count and filp owning the pin */
+ uint32_t user_pin_count;
+ struct drm_file *pin_filp;
};
/**
@@ -414,8 +465,19 @@ struct drm_i915_file_private {
} mm;
};
+enum intel_chip_family {
+ CHIP_I8XX = 0x01,
+ CHIP_I9XX = 0x02,
+ CHIP_I915 = 0x04,
+ CHIP_I965 = 0x08,
+};
+
extern struct drm_ioctl_desc i915_ioctls[];
extern int i915_max_ioctl;
+extern unsigned int i915_fbpercrtc;
+
+extern int i915_master_create(struct drm_device *dev, struct drm_master *master);
+extern void i915_master_destroy(struct drm_device *dev, struct drm_master *master);
/* i915_dma.c */
extern void i915_kernel_lost_context(struct drm_device * dev);
@@ -441,6 +503,7 @@ extern int i915_irq_wait(struct drm_device *dev, void *data,
struct drm_file *file_priv);
void i915_user_irq_get(struct drm_device *dev);
void i915_user_irq_put(struct drm_device *dev);
+extern void i915_enable_interrupt (struct drm_device *dev);
extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS);
extern void i915_driver_irq_preinstall(struct drm_device * dev);
@@ -487,6 +550,8 @@ int i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+int i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
@@ -523,6 +588,16 @@ uint32_t i915_get_gem_seqno(struct drm_device *dev);
void i915_gem_retire_requests(struct drm_device *dev);
void i915_gem_retire_work_handler(struct work_struct *work);
void i915_gem_clflush_object(struct drm_gem_object *obj);
+int i915_gem_object_set_domain(struct drm_gem_object *obj,
+ uint32_t read_domains,
+ uint32_t write_domain);
+int i915_gem_init_ringbuffer(struct drm_device *dev);
+void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
+int i915_gem_do_init(struct drm_device *dev, unsigned long start,
+ unsigned long end);
+int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj,
+ int write);
/* i915_gem_tiling.c */
void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
@@ -561,6 +636,10 @@ static inline void opregion_asle_intr(struct drm_device *dev) { return; }
static inline void opregion_enable_asle(struct drm_device *dev) { return; }
#endif
+/* modesetting */
+extern void intel_modeset_init(struct drm_device *dev);
+extern void intel_modeset_cleanup(struct drm_device *dev);
+
/**
* Lock test for when it's just for synchronization of ring access.
*
@@ -578,6 +657,13 @@ static inline void opregion_enable_asle(struct drm_device *dev) { return; }
#define I915_WRITE16(reg, val) writel(val, dev_priv->regs + (reg))
#define I915_READ8(reg) readb(dev_priv->regs + (reg))
#define I915_WRITE8(reg, val) writeb(val, dev_priv->regs + (reg))
+#ifdef writeq
+#define I915_WRITE64(reg, val) writeq(val, dev_priv->regs + (reg))
+#else
+#define I915_WRITE64(reg, val) (writel(val, dev_priv->regs + (reg)), \
+ writel(upper_32_bits(val), dev_priv->regs + \
+ (reg) + 4))
+#endif
#define I915_VERBOSE 0
@@ -660,7 +746,8 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
#define IS_G4X(dev) ((dev)->pci_device == 0x2E02 || \
(dev)->pci_device == 0x2E12 || \
- (dev)->pci_device == 0x2E22)
+ (dev)->pci_device == 0x2E22 || \
+ IS_GM45(dev))
#define IS_G33(dev) ((dev)->pci_device == 0x29C2 || \
(dev)->pci_device == 0x29B2 || \
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 24fe8c10b4b2..cc2ca5561feb 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -30,6 +30,7 @@
#include "i915_drm.h"
#include "i915_drv.h"
#include <linux/swap.h>
+#include <linux/pci.h>
#define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT))
@@ -40,8 +41,6 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj,
static void i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj);
static void i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj);
static void i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj);
-static int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj,
- int write);
static int i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj,
int write);
static int i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj,
@@ -51,34 +50,43 @@ static void i915_gem_object_set_to_full_cpu_read_domain(struct drm_gem_object *o
static int i915_gem_object_get_page_list(struct drm_gem_object *obj);
static void i915_gem_object_free_page_list(struct drm_gem_object *obj);
static int i915_gem_object_wait_rendering(struct drm_gem_object *obj);
+static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
+ unsigned alignment);
+static void i915_gem_object_get_fence_reg(struct drm_gem_object *obj);
+static void i915_gem_clear_fence_reg(struct drm_gem_object *obj);
+static int i915_gem_evict_something(struct drm_device *dev);
+
+int i915_gem_do_init(struct drm_device *dev, unsigned long start,
+ unsigned long end)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
-static void
-i915_gem_cleanup_ringbuffer(struct drm_device *dev);
+ if (start >= end ||
+ (start & (PAGE_SIZE - 1)) != 0 ||
+ (end & (PAGE_SIZE - 1)) != 0) {
+ return -EINVAL;
+ }
+
+ drm_mm_init(&dev_priv->mm.gtt_space, start,
+ end - start);
+
+ dev->gtt_total = (uint32_t) (end - start);
+
+ return 0;
+}
int
i915_gem_init_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_init *args = data;
+ int ret;
mutex_lock(&dev->struct_mutex);
-
- if (args->gtt_start >= args->gtt_end ||
- (args->gtt_start & (PAGE_SIZE - 1)) != 0 ||
- (args->gtt_end & (PAGE_SIZE - 1)) != 0) {
- mutex_unlock(&dev->struct_mutex);
- return -EINVAL;
- }
-
- drm_mm_init(&dev_priv->mm.gtt_space, args->gtt_start,
- args->gtt_end - args->gtt_start);
-
- dev->gtt_total = (uint32_t) (args->gtt_end - args->gtt_start);
-
+ ret = i915_gem_do_init(dev, args->gtt_start, args->gtt_end);
mutex_unlock(&dev->struct_mutex);
- return 0;
+ return ret;
}
int
@@ -529,6 +537,252 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
return 0;
}
+/**
+ * i915_gem_fault - fault a page into the GTT
+ * vma: VMA in question
+ * vmf: fault info
+ *
+ * The fault handler is set up by drm_gem_mmap() when a object is GTT mapped
+ * from userspace. The fault handler takes care of binding the object to
+ * the GTT (if needed), allocating and programming a fence register (again,
+ * only if needed based on whether the old reg is still valid or the object
+ * is tiled) and inserting a new PTE into the faulting process.
+ *
+ * Note that the faulting process may involve evicting existing objects
+ * from the GTT and/or fence registers to make room. So performance may
+ * suffer if the GTT working set is large or there are few fence registers
+ * left.
+ */
+int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct drm_gem_object *obj = vma->vm_private_data;
+ struct drm_device *dev = obj->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ pgoff_t page_offset;
+ unsigned long pfn;
+ int ret = 0;
+
+ /* We don't use vmf->pgoff since that has the fake offset */
+ page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >>
+ PAGE_SHIFT;
+
+ /* Now bind it into the GTT if needed */
+ mutex_lock(&dev->struct_mutex);
+ if (!obj_priv->gtt_space) {
+ ret = i915_gem_object_bind_to_gtt(obj, obj_priv->gtt_alignment);
+ if (ret) {
+ mutex_unlock(&dev->struct_mutex);
+ return VM_FAULT_SIGBUS;
+ }
+ list_add(&obj_priv->list, &dev_priv->mm.inactive_list);
+ }
+
+ /* Need a new fence register? */
+ if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
+ obj_priv->tiling_mode != I915_TILING_NONE)
+ i915_gem_object_get_fence_reg(obj);
+
+ pfn = ((dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT) +
+ page_offset;
+
+ /* Finally, remap it using the new GTT offset */
+ ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ switch (ret) {
+ case -ENOMEM:
+ case -EAGAIN:
+ return VM_FAULT_OOM;
+ case -EFAULT:
+ case -EBUSY:
+ DRM_ERROR("can't insert pfn?? fault or busy...\n");
+ return VM_FAULT_SIGBUS;
+ default:
+ return VM_FAULT_NOPAGE;
+ }
+}
+
+/**
+ * i915_gem_create_mmap_offset - create a fake mmap offset for an object
+ * @obj: obj in question
+ *
+ * GEM memory mapping works by handing back to userspace a fake mmap offset
+ * it can use in a subsequent mmap(2) call. The DRM core code then looks
+ * up the object based on the offset and sets up the various memory mapping
+ * structures.
+ *
+ * This routine allocates and attaches a fake offset for @obj.
+ */
+static int
+i915_gem_create_mmap_offset(struct drm_gem_object *obj)
+{
+ struct drm_device *dev = obj->dev;
+ struct drm_gem_mm *mm = dev->mm_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ struct drm_map_list *list;
+ struct drm_map *map;
+ int ret = 0;
+
+ /* Set the object up for mmap'ing */
+ list = &obj->map_list;
+ list->map = drm_calloc(1, sizeof(struct drm_map_list),
+ DRM_MEM_DRIVER);
+ if (!list->map)
+ return -ENOMEM;
+
+ map = list->map;
+ map->type = _DRM_GEM;
+ map->size = obj->size;
+ map->handle = obj;
+
+ /* Get a DRM GEM mmap offset allocated... */
+ list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
+ obj->size / PAGE_SIZE, 0, 0);
+ if (!list->file_offset_node) {
+ DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
+ ret = -ENOMEM;
+ goto out_free_list;
+ }
+
+ list->file_offset_node = drm_mm_get_block(list->file_offset_node,
+ obj->size / PAGE_SIZE, 0);
+ if (!list->file_offset_node) {
+ ret = -ENOMEM;
+ goto out_free_list;
+ }
+
+ list->hash.key = list->file_offset_node->start;
+ if (drm_ht_insert_item(&mm->offset_hash, &list->hash)) {
+ DRM_ERROR("failed to add to map hash\n");
+ goto out_free_mm;
+ }
+
+ /* By now we should be all set, any drm_mmap request on the offset
+ * below will get to our mmap & fault handler */
+ obj_priv->mmap_offset = ((uint64_t) list->hash.key) << PAGE_SHIFT;
+
+ return 0;
+
+out_free_mm:
+ drm_mm_put_block(list->file_offset_node);
+out_free_list:
+ drm_free(list->map, sizeof(struct drm_map_list), DRM_MEM_DRIVER);
+
+ return ret;
+}
+
+/**
+ * i915_gem_get_gtt_alignment - return required GTT alignment for an object
+ * @obj: object to check
+ *
+ * Return the required GTT alignment for an object, taking into account
+ * potential fence register mapping if needed.
+ */
+static uint32_t
+i915_gem_get_gtt_alignment(struct drm_gem_object *obj)
+{
+ struct drm_device *dev = obj->dev;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ int start, i;
+
+ /*
+ * Minimum alignment is 4k (GTT page size), but might be greater
+ * if a fence register is needed for the object.
+ */
+ if (IS_I965G(dev) || obj_priv->tiling_mode == I915_TILING_NONE)
+ return 4096;
+
+ /*
+ * Previous chips need to be aligned to the size of the smallest
+ * fence register that can contain the object.
+ */
+ if (IS_I9XX(dev))
+ start = 1024*1024;
+ else
+ start = 512*1024;
+
+ for (i = start; i < obj->size; i <<= 1)
+ ;
+
+ return i;
+}
+
+/**
+ * i915_gem_mmap_gtt_ioctl - prepare an object for GTT mmap'ing
+ * @dev: DRM device
+ * @data: GTT mapping ioctl data
+ * @file_priv: GEM object info
+ *
+ * Simply returns the fake offset to userspace so it can mmap it.
+ * The mmap call will end up in drm_gem_mmap(), which will set things
+ * up so we can get faults in the handler above.
+ *
+ * The fault handler will take care of binding the object into the GTT
+ * (since it may have been evicted to make room for something), allocating
+ * a fence register, and mapping the appropriate aperture address into
+ * userspace.
+ */
+int
+i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_i915_gem_mmap_gtt *args = data;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_gem_object *obj;
+ struct drm_i915_gem_object *obj_priv;
+ int ret;
+
+ if (!(dev->driver->driver_features & DRIVER_GEM))
+ return -ENODEV;
+
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (obj == NULL)
+ return -EBADF;
+
+ mutex_lock(&dev->struct_mutex);
+
+ obj_priv = obj->driver_private;
+
+ if (!obj_priv->mmap_offset) {
+ ret = i915_gem_create_mmap_offset(obj);
+ if (ret)
+ return ret;
+ }
+
+ args->offset = obj_priv->mmap_offset;
+
+ obj_priv->gtt_alignment = i915_gem_get_gtt_alignment(obj);
+
+ /* Make sure the alignment is correct for fence regs etc */
+ if (obj_priv->agp_mem &&
+ (obj_priv->gtt_offset & (obj_priv->gtt_alignment - 1))) {
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return -EINVAL;
+ }
+
+ /*
+ * Pull it into the GTT so that we have a page list (makes the
+ * initial fault faster and any subsequent flushing possible).
+ */
+ if (!obj_priv->agp_mem) {
+ ret = i915_gem_object_bind_to_gtt(obj, obj_priv->gtt_alignment);
+ if (ret) {
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
+ list_add(&obj_priv->list, &dev_priv->mm.inactive_list);
+ }
+
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
static void
i915_gem_object_free_page_list(struct drm_gem_object *obj)
{
@@ -726,6 +980,7 @@ i915_gem_retire_request(struct drm_device *dev,
*/
if (obj_priv->last_rendering_seqno != request->seqno)
return;
+
#if WATCH_LRU
DRM_INFO("%s: retire %d moves to inactive list %p\n",
__func__, request->seqno, obj);
@@ -956,6 +1211,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ loff_t offset;
int ret = 0;
#if WATCH_BUF
@@ -991,6 +1247,14 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
BUG_ON(obj_priv->active);
+ /* blow away mappings if mapped through GTT */
+ offset = ((loff_t) obj->map_list.hash.key) << PAGE_SHIFT;
+ if (dev->dev_mapping)
+ unmap_mapping_range(dev->dev_mapping, offset, obj->size, 1);
+
+ if (obj_priv->fence_reg != I915_FENCE_REG_NONE)
+ i915_gem_clear_fence_reg(obj);
+
i915_gem_object_free_page_list(obj);
if (obj_priv->gtt_space) {
@@ -1149,6 +1413,204 @@ i915_gem_object_get_page_list(struct drm_gem_object *obj)
return 0;
}
+static void i965_write_fence_reg(struct drm_i915_fence_reg *reg)
+{
+ struct drm_gem_object *obj = reg->obj;
+ struct drm_device *dev = obj->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ int regnum = obj_priv->fence_reg;
+ uint64_t val;
+
+ val = (uint64_t)((obj_priv->gtt_offset + obj->size - 4096) &
+ 0xfffff000) << 32;
+ val |= obj_priv->gtt_offset & 0xfffff000;
+ val |= ((obj_priv->stride / 128) - 1) << I965_FENCE_PITCH_SHIFT;
+ if (obj_priv->tiling_mode == I915_TILING_Y)
+ val |= 1 << I965_FENCE_TILING_Y_SHIFT;
+ val |= I965_FENCE_REG_VALID;
+
+ I915_WRITE64(FENCE_REG_965_0 + (regnum * 8), val);
+}
+
+static void i915_write_fence_reg(struct drm_i915_fence_reg *reg)
+{
+ struct drm_gem_object *obj = reg->obj;
+ struct drm_device *dev = obj->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ int regnum = obj_priv->fence_reg;
+ uint32_t val;
+ uint32_t pitch_val;
+
+ if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) ||
+ (obj_priv->gtt_offset & (obj->size - 1))) {
+ WARN(1, "%s: object not 1M or size aligned\n", __FUNCTION__);
+ return;
+ }
+
+ if (obj_priv->tiling_mode == I915_TILING_Y && (IS_I945G(dev) ||
+ IS_I945GM(dev) ||
+ IS_G33(dev)))
+ pitch_val = (obj_priv->stride / 128) - 1;
+ else
+ pitch_val = (obj_priv->stride / 512) - 1;
+
+ val = obj_priv->gtt_offset;
+ if (obj_priv->tiling_mode == I915_TILING_Y)
+ val |= 1 << I830_FENCE_TILING_Y_SHIFT;
+ val |= I915_FENCE_SIZE_BITS(obj->size);
+ val |= pitch_val << I830_FENCE_PITCH_SHIFT;
+ val |= I830_FENCE_REG_VALID;
+
+ I915_WRITE(FENCE_REG_830_0 + (regnum * 4), val);
+}
+
+static void i830_write_fence_reg(struct drm_i915_fence_reg *reg)
+{
+ struct drm_gem_object *obj = reg->obj;
+ struct drm_device *dev = obj->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ int regnum = obj_priv->fence_reg;
+ uint32_t val;
+ uint32_t pitch_val;
+
+ if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) ||
+ (obj_priv->gtt_offset & (obj->size - 1))) {
+ WARN(1, "%s: object not 1M or size aligned\n", __FUNCTION__);
+ return;
+ }
+
+ pitch_val = (obj_priv->stride / 128) - 1;
+
+ val = obj_priv->gtt_offset;
+ if (obj_priv->tiling_mode == I915_TILING_Y)
+ val |= 1 << I830_FENCE_TILING_Y_SHIFT;
+ val |= I830_FENCE_SIZE_BITS(obj->size);
+ val |= pitch_val << I830_FENCE_PITCH_SHIFT;
+ val |= I830_FENCE_REG_VALID;
+
+ I915_WRITE(FENCE_REG_830_0 + (regnum * 4), val);
+
+}
+
+/**
+ * i915_gem_object_get_fence_reg - set up a fence reg for an object
+ * @obj: object to map through a fence reg
+ *
+ * When mapping objects through the GTT, userspace wants to be able to write
+ * to them without having to worry about swizzling if the object is tiled.
+ *
+ * This function walks the fence regs looking for a free one for @obj,
+ * stealing one if it can't find any.
+ *
+ * It then sets up the reg based on the object's properties: address, pitch
+ * and tiling format.
+ */
+static void
+i915_gem_object_get_fence_reg(struct drm_gem_object *obj)
+{
+ struct drm_device *dev = obj->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ struct drm_i915_fence_reg *reg = NULL;
+ int i, ret;
+
+ switch (obj_priv->tiling_mode) {
+ case I915_TILING_NONE:
+ WARN(1, "allocating a fence for non-tiled object?\n");
+ break;
+ case I915_TILING_X:
+ WARN(obj_priv->stride & (512 - 1),
+ "object is X tiled but has non-512B pitch\n");
+ break;
+ case I915_TILING_Y:
+ WARN(obj_priv->stride & (128 - 1),
+ "object is Y tiled but has non-128B pitch\n");
+ break;
+ }
+
+ /* First try to find a free reg */
+ for (i = dev_priv->fence_reg_start; i < dev_priv->num_fence_regs; i++) {
+ reg = &dev_priv->fence_regs[i];
+ if (!reg->obj)
+ break;
+ }
+
+ /* None available, try to steal one or wait for a user to finish */
+ if (i == dev_priv->num_fence_regs) {
+ struct drm_i915_gem_object *old_obj_priv = NULL;
+ loff_t offset;
+
+try_again:
+ /* Could try to use LRU here instead... */
+ for (i = dev_priv->fence_reg_start;
+ i < dev_priv->num_fence_regs; i++) {
+ reg = &dev_priv->fence_regs[i];
+ old_obj_priv = reg->obj->driver_private;
+ if (!old_obj_priv->pin_count)
+ break;
+ }
+
+ /*
+ * Now things get ugly... we have to wait for one of the
+ * objects to finish before trying again.
+ */
+ if (i == dev_priv->num_fence_regs) {
+ ret = i915_gem_object_wait_rendering(reg->obj);
+ if (ret) {
+ WARN(ret, "wait_rendering failed: %d\n", ret);
+ return;
+ }
+ goto try_again;
+ }
+
+ /*
+ * Zap this virtual mapping so we can set up a fence again
+ * for this object next time we need it.
+ */
+ offset = ((loff_t) reg->obj->map_list.hash.key) << PAGE_SHIFT;
+ if (dev->dev_mapping)
+ unmap_mapping_range(dev->dev_mapping, offset,
+ reg->obj->size, 1);
+ old_obj_priv->fence_reg = I915_FENCE_REG_NONE;
+ }
+
+ obj_priv->fence_reg = i;
+ reg->obj = obj;
+
+ if (IS_I965G(dev))
+ i965_write_fence_reg(reg);
+ else if (IS_I9XX(dev))
+ i915_write_fence_reg(reg);
+ else
+ i830_write_fence_reg(reg);
+}
+
+/**
+ * i915_gem_clear_fence_reg - clear out fence register info
+ * @obj: object to clear
+ *
+ * Zeroes out the fence register itself and clears out the associated
+ * data structures in dev_priv and obj_priv.
+ */
+static void
+i915_gem_clear_fence_reg(struct drm_gem_object *obj)
+{
+ struct drm_device *dev = obj->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+
+ if (IS_I965G(dev))
+ I915_WRITE64(FENCE_REG_965_0 + (obj_priv->fence_reg * 8), 0);
+ else
+ I915_WRITE(FENCE_REG_830_0 + (obj_priv->fence_reg * 4), 0);
+
+ dev_priv->fence_regs[obj_priv->fence_reg].obj = NULL;
+ obj_priv->fence_reg = I915_FENCE_REG_NONE;
+}
+
/**
* Finds free space in the GTT aperture and binds the object there.
*/
@@ -1307,7 +1769,7 @@ i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj)
* This function returns when the move is complete, including waiting on
* flushes to occur.
*/
-static int
+int
i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write)
{
struct drm_i915_gem_object *obj_priv = obj->driver_private;
@@ -2029,13 +2491,15 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
/* error other than GTT full, or we've already tried again */
if (ret != -ENOMEM || pin_tries >= 1) {
- DRM_ERROR("Failed to pin buffers %d\n", ret);
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("Failed to pin buffers %d\n", ret);
goto err;
}
/* unpin all of our buffers */
for (i = 0; i < pinned; i++)
i915_gem_object_unpin(object_list[i]);
+ pinned = 0;
/* evict everyone we can from the aperture */
ret = i915_gem_evict_everything(dev);
@@ -2149,13 +2613,12 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
"back to user (%d)\n",
args->buffer_count, ret);
err:
- if (object_list != NULL) {
- for (i = 0; i < pinned; i++)
- i915_gem_object_unpin(object_list[i]);
+ for (i = 0; i < pinned; i++)
+ i915_gem_object_unpin(object_list[i]);
+
+ for (i = 0; i < args->buffer_count; i++)
+ drm_gem_object_unreference(object_list[i]);
- for (i = 0; i < args->buffer_count; i++)
- drm_gem_object_unreference(object_list[i]);
- }
mutex_unlock(&dev->struct_mutex);
pre_mutex_err:
@@ -2178,7 +2641,8 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment)
if (obj_priv->gtt_space == NULL) {
ret = i915_gem_object_bind_to_gtt(obj, alignment);
if (ret != 0) {
- DRM_ERROR("Failure to bind: %d", ret);
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("Failure to bind: %d", ret);
return ret;
}
}
@@ -2249,11 +2713,22 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
}
obj_priv = obj->driver_private;
- ret = i915_gem_object_pin(obj, args->alignment);
- if (ret != 0) {
- drm_gem_object_unreference(obj);
+ if (obj_priv->pin_filp != NULL && obj_priv->pin_filp != file_priv) {
+ DRM_ERROR("Already pinned in i915_gem_pin_ioctl(): %d\n",
+ args->handle);
mutex_unlock(&dev->struct_mutex);
- return ret;
+ return -EINVAL;
+ }
+
+ obj_priv->user_pin_count++;
+ obj_priv->pin_filp = file_priv;
+ if (obj_priv->user_pin_count == 1) {
+ ret = i915_gem_object_pin(obj, args->alignment);
+ if (ret != 0) {
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
}
/* XXX - flush the CPU caches for pinned objects
@@ -2273,6 +2748,7 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
{
struct drm_i915_gem_pin *args = data;
struct drm_gem_object *obj;
+ struct drm_i915_gem_object *obj_priv;
mutex_lock(&dev->struct_mutex);
@@ -2284,7 +2760,19 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
return -EBADF;
}
- i915_gem_object_unpin(obj);
+ obj_priv = obj->driver_private;
+ if (obj_priv->pin_filp != file_priv) {
+ DRM_ERROR("Not pinned by caller in i915_gem_pin_ioctl(): %d\n",
+ args->handle);
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return -EINVAL;
+ }
+ obj_priv->user_pin_count--;
+ if (obj_priv->user_pin_count == 0) {
+ obj_priv->pin_filp = NULL;
+ i915_gem_object_unpin(obj);
+ }
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
@@ -2351,12 +2839,18 @@ int i915_gem_init_object(struct drm_gem_object *obj)
obj->driver_private = obj_priv;
obj_priv->obj = obj;
+ obj_priv->fence_reg = I915_FENCE_REG_NONE;
INIT_LIST_HEAD(&obj_priv->list);
+
return 0;
}
void i915_gem_free_object(struct drm_gem_object *obj)
{
+ struct drm_device *dev = obj->dev;
+ struct drm_gem_mm *mm = dev->mm_private;
+ struct drm_map_list *list;
+ struct drm_map *map;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
while (obj_priv->pin_count > 0)
@@ -2364,6 +2858,20 @@ void i915_gem_free_object(struct drm_gem_object *obj)
i915_gem_object_unbind(obj);
+ list = &obj->map_list;
+ drm_ht_remove_item(&mm->offset_hash, &list->hash);
+
+ if (list->file_offset_node) {
+ drm_mm_put_block(list->file_offset_node);
+ list->file_offset_node = NULL;
+ }
+
+ map = list->map;
+ if (map) {
+ drm_free(map, sizeof(*map), DRM_MEM_DRIVER);
+ list->map = NULL;
+ }
+
drm_free(obj_priv->page_cpu_valid, 1, DRM_MEM_DRIVER);
drm_free(obj->driver_private, 1, DRM_MEM_DRIVER);
}
@@ -2432,8 +2940,7 @@ i915_gem_idle(struct drm_device *dev)
*/
i915_gem_flush(dev, ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT),
~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT));
- seqno = i915_add_request(dev, ~(I915_GEM_DOMAIN_CPU |
- I915_GEM_DOMAIN_GTT));
+ seqno = i915_add_request(dev, ~I915_GEM_DOMAIN_CPU);
if (seqno == 0) {
mutex_unlock(&dev->struct_mutex);
@@ -2560,12 +3067,13 @@ i915_gem_init_hws(struct drm_device *dev)
return 0;
}
-static int
+int
i915_gem_init_ringbuffer(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
+ drm_i915_ring_buffer_t *ring = &dev_priv->ring;
int ret;
u32 head;
@@ -2587,24 +3095,24 @@ i915_gem_init_ringbuffer(struct drm_device *dev)
}
/* Set up the kernel mapping for the ring. */
- dev_priv->ring.Size = obj->size;
- dev_priv->ring.tail_mask = obj->size - 1;
+ ring->Size = obj->size;
+ ring->tail_mask = obj->size - 1;
- dev_priv->ring.map.offset = dev->agp->base + obj_priv->gtt_offset;
- dev_priv->ring.map.size = obj->size;
- dev_priv->ring.map.type = 0;
- dev_priv->ring.map.flags = 0;
- dev_priv->ring.map.mtrr = 0;
+ ring->map.offset = dev->agp->base + obj_priv->gtt_offset;
+ ring->map.size = obj->size;
+ ring->map.type = 0;
+ ring->map.flags = 0;
+ ring->map.mtrr = 0;
- drm_core_ioremap_wc(&dev_priv->ring.map, dev);
- if (dev_priv->ring.map.handle == NULL) {
+ drm_core_ioremap_wc(&ring->map, dev);
+ if (ring->map.handle == NULL) {
DRM_ERROR("Failed to map ringbuffer.\n");
memset(&dev_priv->ring, 0, sizeof(dev_priv->ring));
drm_gem_object_unreference(obj);
return -EINVAL;
}
- dev_priv->ring.ring_obj = obj;
- dev_priv->ring.virtual_start = dev_priv->ring.map.handle;
+ ring->ring_obj = obj;
+ ring->virtual_start = ring->map.handle;
/* Stop the ring if it's running. */
I915_WRITE(PRB0_CTL, 0);
@@ -2652,12 +3160,20 @@ i915_gem_init_ringbuffer(struct drm_device *dev)
}
/* Update our cache of the ring state */
- i915_kernel_lost_context(dev);
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ i915_kernel_lost_context(dev);
+ else {
+ ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR;
+ ring->tail = I915_READ(PRB0_TAIL) & TAIL_ADDR;
+ ring->space = ring->head - (ring->tail + 8);
+ if (ring->space < 0)
+ ring->space += ring->Size;
+ }
return 0;
}
-static void
+void
i915_gem_cleanup_ringbuffer(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
@@ -2695,6 +3211,9 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
drm_i915_private_t *dev_priv = dev->dev_private;
int ret;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
+
if (dev_priv->mm.wedged) {
DRM_ERROR("Reenabling wedged hardware, good luck\n");
dev_priv->mm.wedged = 0;
@@ -2728,6 +3247,9 @@ i915_gem_leavevt_ioctl(struct drm_device *dev, void *data,
drm_i915_private_t *dev_priv = dev->dev_private;
int ret;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
+
ret = i915_gem_idle(dev);
drm_irq_uninstall(dev);
@@ -2758,5 +3280,13 @@ i915_gem_load(struct drm_device *dev)
i915_gem_retire_work_handler);
dev_priv->mm.next_gem_seqno = 1;
+ /* Old X drivers will take 0-2 for front, back, depth buffers */
+ dev_priv->fence_reg_start = 3;
+
+ if (IS_I965G(dev))
+ dev_priv->num_fence_regs = 16;
+ else
+ dev_priv->num_fence_regs = 8;
+
i915_gem_detect_bit_6_swizzle(dev);
}
diff --git a/drivers/gpu/drm/i915/i915_gem_proc.c b/drivers/gpu/drm/i915/i915_gem_proc.c
index e8d5abe1250e..4d1b9de0cd8b 100644
--- a/drivers/gpu/drm/i915/i915_gem_proc.c
+++ b/drivers/gpu/drm/i915/i915_gem_proc.c
@@ -250,6 +250,39 @@ static int i915_interrupt_info(char *buf, char **start, off_t offset,
return len - offset;
}
+static int i915_hws_info(char *buf, char **start, off_t offset,
+ int request, int *eof, void *data)
+{
+ struct drm_minor *minor = (struct drm_minor *) data;
+ struct drm_device *dev = minor->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ int len = 0, i;
+ volatile u32 *hws;
+
+ if (offset > DRM_PROC_LIMIT) {
+ *eof = 1;
+ return 0;
+ }
+
+ hws = (volatile u32 *)dev_priv->hw_status_page;
+ if (hws == NULL) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = &buf[offset];
+ *eof = 0;
+ for (i = 0; i < 4096 / sizeof(u32) / 4; i += 4) {
+ DRM_PROC_PRINT("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ i * 4,
+ hws[i], hws[i + 1], hws[i + 2], hws[i + 3]);
+ }
+ if (len > request + offset)
+ return request;
+ *eof = 1;
+ return len - offset;
+}
+
static struct drm_proc_list {
/** file name */
const char *name;
@@ -262,6 +295,7 @@ static struct drm_proc_list {
{"i915_gem_request", i915_gem_request_info},
{"i915_gem_seqno", i915_gem_seqno_info},
{"i915_gem_interrupt", i915_interrupt_info},
+ {"i915_gem_hws", i915_hws_info},
};
#define I915_GEM_PROC_ENTRIES ARRAY_SIZE(i915_gem_proc_list)
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index a8cb69469c64..241f39b7f460 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -208,6 +208,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
}
}
obj_priv->tiling_mode = args->tiling_mode;
+ obj_priv->stride = args->stride;
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 69b9a42da95e..0cadafbef411 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -30,6 +30,7 @@
#include "drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
+#include "intel_drv.h"
#define MAX_NOPID ((u32)~0)
@@ -51,6 +52,15 @@
#define I915_INTERRUPT_ENABLE_MASK (I915_INTERRUPT_ENABLE_FIX | \
I915_INTERRUPT_ENABLE_VAR)
+#define I915_PIPE_VBLANK_STATUS (PIPE_START_VBLANK_INTERRUPT_STATUS |\
+ PIPE_VBLANK_INTERRUPT_STATUS)
+
+#define I915_PIPE_VBLANK_ENABLE (PIPE_START_VBLANK_INTERRUPT_ENABLE |\
+ PIPE_VBLANK_INTERRUPT_ENABLE)
+
+#define DRM_I915_VBLANK_PIPE_ALL (DRM_I915_VBLANK_PIPE_A | \
+ DRM_I915_VBLANK_PIPE_B)
+
void
i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
{
@@ -168,6 +178,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *) arg;
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_master_private *master_priv;
u32 iir, new_iir;
u32 pipea_stats, pipeb_stats;
u32 vblank_status;
@@ -200,6 +211,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
pipea_stats = I915_READ(PIPEASTAT);
pipeb_stats = I915_READ(PIPEBSTAT);
+
/*
* Clear the PIPE(A|B)STAT regs before the IIR
*/
@@ -222,9 +234,12 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
I915_WRITE(IIR, iir);
new_iir = I915_READ(IIR); /* Flush posted writes */
- if (dev_priv->sarea_priv)
- dev_priv->sarea_priv->last_dispatch =
- READ_BREADCRUMB(dev_priv);
+ if (dev->primary->master) {
+ master_priv = dev->primary->master->driver_priv;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->last_dispatch =
+ READ_BREADCRUMB(dev_priv);
+ }
if (iir & I915_USER_INTERRUPT) {
dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev);
@@ -269,6 +284,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
static int i915_emit_irq(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
RING_LOCALS;
i915_kernel_lost_context(dev);
@@ -278,8 +294,8 @@ static int i915_emit_irq(struct drm_device * dev)
dev_priv->counter++;
if (dev_priv->counter > 0x7FFFFFFFUL)
dev_priv->counter = 1;
- if (dev_priv->sarea_priv)
- dev_priv->sarea_priv->last_enqueue = dev_priv->counter;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->last_enqueue = dev_priv->counter;
BEGIN_LP_RING(4);
OUT_RING(MI_STORE_DWORD_INDEX);
@@ -317,21 +333,20 @@ void i915_user_irq_put(struct drm_device *dev)
static int i915_wait_irq(struct drm_device * dev, int irq_nr)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
int ret = 0;
DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
READ_BREADCRUMB(dev_priv));
if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
- if (dev_priv->sarea_priv) {
- dev_priv->sarea_priv->last_dispatch =
- READ_BREADCRUMB(dev_priv);
- }
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
return 0;
}
- if (dev_priv->sarea_priv)
- dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
i915_user_irq_get(dev);
DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
@@ -343,10 +358,6 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
READ_BREADCRUMB(dev_priv), (int)dev_priv->counter);
}
- if (dev_priv->sarea_priv)
- dev_priv->sarea_priv->last_dispatch =
- READ_BREADCRUMB(dev_priv);
-
return ret;
}
@@ -427,6 +438,14 @@ void i915_disable_vblank(struct drm_device *dev, int pipe)
spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
}
+void i915_enable_interrupt (struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ opregion_enable_asle(dev);
+ dev_priv->irq_enabled = 1;
+}
+
+
/* Set the vblank monitor pipe
*/
int i915_vblank_pipe_set(struct drm_device *dev, void *data,
@@ -487,6 +506,8 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ atomic_set(&dev_priv->irq_received, 0);
+
I915_WRITE(HWSTAM, 0xeffe);
I915_WRITE(PIPEASTAT, 0);
I915_WRITE(PIPEBSTAT, 0);
diff --git a/drivers/gpu/drm/i915/i915_mem.c b/drivers/gpu/drm/i915/i915_mem.c
index 6126a60dc9cb..96e271986d2a 100644
--- a/drivers/gpu/drm/i915/i915_mem.c
+++ b/drivers/gpu/drm/i915/i915_mem.c
@@ -46,7 +46,8 @@
static void mark_block(struct drm_device * dev, struct mem_block *p, int in_use)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
+ drm_i915_sarea_t *sarea_priv = master_priv->sarea_priv;
struct drm_tex_region *list;
unsigned shift, nr;
unsigned start;
diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c
index 13ae731a33db..ff012835a386 100644
--- a/drivers/gpu/drm/i915/i915_opregion.c
+++ b/drivers/gpu/drm/i915/i915_opregion.c
@@ -257,8 +257,8 @@ void opregion_enable_asle(struct drm_device *dev)
static struct intel_opregion *system_opregion;
-int intel_opregion_video_event(struct notifier_block *nb, unsigned long val,
- void *data)
+static int intel_opregion_video_event(struct notifier_block *nb,
+ unsigned long val, void *data)
{
/* The only video events relevant to opregion are 0x80. These indicate
either a docking event, lid switch or display switch request. In
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 9d24aaeb8a45..47e6bafeb743 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -175,9 +175,26 @@
#define DISPLAY_PLANE_B (1<<20)
/*
- * Instruction and interrupt control regs
+ * Fence registers
*/
+#define FENCE_REG_830_0 0x2000
+#define I830_FENCE_START_MASK 0x07f80000
+#define I830_FENCE_TILING_Y_SHIFT 12
+#define I830_FENCE_SIZE_BITS(size) ((get_order(size >> 19) - 1) << 8)
+#define I830_FENCE_PITCH_SHIFT 4
+#define I830_FENCE_REG_VALID (1<<0)
+
+#define I915_FENCE_START_MASK 0x0ff00000
+#define I915_FENCE_SIZE_BITS(size) ((get_order(size >> 20) - 1) << 8)
+#define FENCE_REG_965_0 0x03000
+#define I965_FENCE_PITCH_SHIFT 2
+#define I965_FENCE_TILING_Y_SHIFT 1
+#define I965_FENCE_REG_VALID (1<<0)
+
+/*
+ * Instruction and interrupt control regs
+ */
#define PRB0_TAIL 0x02030
#define PRB0_HEAD 0x02034
#define PRB0_START 0x02038
@@ -245,6 +262,7 @@
#define CM0_RC_OP_FLUSH_DISABLE (1<<0)
#define GFX_FLSH_CNTL 0x02170 /* 915+ only */
+
/*
* Framebuffer compression (915+ only)
*/
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
new file mode 100644
index 000000000000..4ca82a025525
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+#include "drmP.h"
+#include "drm.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "intel_bios.h"
+
+
+static void *
+find_section(struct bdb_header *bdb, int section_id)
+{
+ u8 *base = (u8 *)bdb;
+ int index = 0;
+ u16 total, current_size;
+ u8 current_id;
+
+ /* skip to first section */
+ index += bdb->header_size;
+ total = bdb->bdb_size;
+
+ /* walk the sections looking for section_id */
+ while (index < total) {
+ current_id = *(base + index);
+ index++;
+ current_size = *((u16 *)(base + index));
+ index += 2;
+ if (current_id == section_id)
+ return base + index;
+ index += current_size;
+ }
+
+ return NULL;
+}
+
+/* Try to find panel data */
+static void
+parse_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
+{
+ struct bdb_lvds_options *lvds_options;
+ struct bdb_lvds_lfp_data *lvds_lfp_data;
+ struct bdb_lvds_lfp_data_entry *entry;
+ struct lvds_dvo_timing *dvo_timing;
+ struct drm_display_mode *panel_fixed_mode;
+
+ /* Defaults if we can't find VBT info */
+ dev_priv->lvds_dither = 0;
+ dev_priv->lvds_vbt = 0;
+
+ lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
+ if (!lvds_options)
+ return;
+
+ dev_priv->lvds_dither = lvds_options->pixel_dither;
+ if (lvds_options->panel_type == 0xff)
+ return;
+
+ lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
+ if (!lvds_lfp_data)
+ return;
+
+ dev_priv->lvds_vbt = 1;
+
+ entry = &lvds_lfp_data->data[lvds_options->panel_type];
+ dvo_timing = &entry->dvo_timing;
+
+ panel_fixed_mode = drm_calloc(1, sizeof(*panel_fixed_mode),
+ DRM_MEM_DRIVER);
+
+ panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) |
+ dvo_timing->hactive_lo;
+ panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay +
+ ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo);
+ panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start +
+ dvo_timing->hsync_pulse_width;
+ panel_fixed_mode->htotal = panel_fixed_mode->hdisplay +
+ ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo);
+
+ panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) |
+ dvo_timing->vactive_lo;
+ panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay +
+ dvo_timing->vsync_off;
+ panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start +
+ dvo_timing->vsync_pulse_width;
+ panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay +
+ ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo);
+ panel_fixed_mode->clock = dvo_timing->clock * 10;
+ panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_set_name(panel_fixed_mode);
+
+ dev_priv->vbt_mode = panel_fixed_mode;
+
+ DRM_DEBUG("Found panel mode in BIOS VBT tables:\n");
+ drm_mode_debug_printmodeline(panel_fixed_mode);
+
+ return;
+}
+
+static void
+parse_general_features(struct drm_i915_private *dev_priv,
+ struct bdb_header *bdb)
+{
+ struct bdb_general_features *general;
+
+ /* Set sensible defaults in case we can't find the general block */
+ dev_priv->int_tv_support = 1;
+ dev_priv->int_crt_support = 1;
+
+ general = find_section(bdb, BDB_GENERAL_FEATURES);
+ if (general) {
+ dev_priv->int_tv_support = general->int_tv_support;
+ dev_priv->int_crt_support = general->int_crt_support;
+ }
+}
+
+/**
+ * intel_init_bios - initialize VBIOS settings & find VBT
+ * @dev: DRM device
+ *
+ * Loads the Video BIOS and checks that the VBT exists. Sets scratch registers
+ * to appropriate values.
+ *
+ * VBT existence is a sanity check that is relied on by other i830_bios.c code.
+ * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may
+ * feed an updated VBT back through that, compared to what we'll fetch using
+ * this method of groping around in the BIOS data.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+bool
+intel_init_bios(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct pci_dev *pdev = dev->pdev;
+ struct vbt_header *vbt = NULL;
+ struct bdb_header *bdb;
+ u8 __iomem *bios;
+ size_t size;
+ int i;
+
+ bios = pci_map_rom(pdev, &size);
+ if (!bios)
+ return -1;
+
+ /* Scour memory looking for the VBT signature */
+ for (i = 0; i + 4 < size; i++) {
+ if (!memcmp(bios + i, "$VBT", 4)) {
+ vbt = (struct vbt_header *)(bios + i);
+ break;
+ }
+ }
+
+ if (!vbt) {
+ DRM_ERROR("VBT signature missing\n");
+ pci_unmap_rom(pdev, bios);
+ return -1;
+ }
+
+ bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
+
+ /* Grab useful general definitions */
+ parse_general_features(dev_priv, bdb);
+ parse_panel_data(dev_priv, bdb);
+
+ pci_unmap_rom(pdev, bios);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
new file mode 100644
index 000000000000..5ea715ace3a0
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_bios.h
@@ -0,0 +1,405 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+#ifndef _I830_BIOS_H_
+#define _I830_BIOS_H_
+
+#include "drmP.h"
+
+struct vbt_header {
+ u8 signature[20]; /**< Always starts with 'VBT$' */
+ u16 version; /**< decimal */
+ u16 header_size; /**< in bytes */
+ u16 vbt_size; /**< in bytes */
+ u8 vbt_checksum;
+ u8 reserved0;
+ u32 bdb_offset; /**< from beginning of VBT */
+ u32 aim_offset[4]; /**< from beginning of VBT */
+} __attribute__((packed));
+
+struct bdb_header {
+ u8 signature[16]; /**< Always 'BIOS_DATA_BLOCK' */
+ u16 version; /**< decimal */
+ u16 header_size; /**< in bytes */
+ u16 bdb_size; /**< in bytes */
+};
+
+/* strictly speaking, this is a "skip" block, but it has interesting info */
+struct vbios_data {
+ u8 type; /* 0 == desktop, 1 == mobile */
+ u8 relstage;
+ u8 chipset;
+ u8 lvds_present:1;
+ u8 tv_present:1;
+ u8 rsvd2:6; /* finish byte */
+ u8 rsvd3[4];
+ u8 signon[155];
+ u8 copyright[61];
+ u16 code_segment;
+ u8 dos_boot_mode;
+ u8 bandwidth_percent;
+ u8 rsvd4; /* popup memory size */
+ u8 resize_pci_bios;
+ u8 rsvd5; /* is crt already on ddc2 */
+} __attribute__((packed));
+
+/*
+ * There are several types of BIOS data blocks (BDBs), each block has
+ * an ID and size in the first 3 bytes (ID in first, size in next 2).
+ * Known types are listed below.
+ */
+#define BDB_GENERAL_FEATURES 1
+#define BDB_GENERAL_DEFINITIONS 2
+#define BDB_OLD_TOGGLE_LIST 3
+#define BDB_MODE_SUPPORT_LIST 4
+#define BDB_GENERIC_MODE_TABLE 5
+#define BDB_EXT_MMIO_REGS 6
+#define BDB_SWF_IO 7
+#define BDB_SWF_MMIO 8
+#define BDB_DOT_CLOCK_TABLE 9
+#define BDB_MODE_REMOVAL_TABLE 10
+#define BDB_CHILD_DEVICE_TABLE 11
+#define BDB_DRIVER_FEATURES 12
+#define BDB_DRIVER_PERSISTENCE 13
+#define BDB_EXT_TABLE_PTRS 14
+#define BDB_DOT_CLOCK_OVERRIDE 15
+#define BDB_DISPLAY_SELECT 16
+/* 17 rsvd */
+#define BDB_DRIVER_ROTATION 18
+#define BDB_DISPLAY_REMOVE 19
+#define BDB_OEM_CUSTOM 20
+#define BDB_EFP_LIST 21 /* workarounds for VGA hsync/vsync */
+#define BDB_SDVO_LVDS_OPTIONS 22
+#define BDB_SDVO_PANEL_DTDS 23
+#define BDB_SDVO_LVDS_PNP_IDS 24
+#define BDB_SDVO_LVDS_POWER_SEQ 25
+#define BDB_TV_OPTIONS 26
+#define BDB_LVDS_OPTIONS 40
+#define BDB_LVDS_LFP_DATA_PTRS 41
+#define BDB_LVDS_LFP_DATA 42
+#define BDB_LVDS_BACKLIGHT 43
+#define BDB_LVDS_POWER 44
+#define BDB_SKIP 254 /* VBIOS private block, ignore */
+
+struct bdb_general_features {
+ /* bits 1 */
+ u8 panel_fitting:2;
+ u8 flexaim:1;
+ u8 msg_enable:1;
+ u8 clear_screen:3;
+ u8 color_flip:1;
+
+ /* bits 2 */
+ u8 download_ext_vbt:1;
+ u8 enable_ssc:1;
+ u8 ssc_freq:1;
+ u8 enable_lfp_on_override:1;
+ u8 disable_ssc_ddt:1;
+ u8 rsvd8:3; /* finish byte */
+
+ /* bits 3 */
+ u8 disable_smooth_vision:1;
+ u8 single_dvi:1;
+ u8 rsvd9:6; /* finish byte */
+
+ /* bits 4 */
+ u8 legacy_monitor_detect;
+
+ /* bits 5 */
+ u8 int_crt_support:1;
+ u8 int_tv_support:1;
+ u8 rsvd11:6; /* finish byte */
+} __attribute__((packed));
+
+struct bdb_general_definitions {
+ /* DDC GPIO */
+ u8 crt_ddc_gmbus_pin;
+
+ /* DPMS bits */
+ u8 dpms_acpi:1;
+ u8 skip_boot_crt_detect:1;
+ u8 dpms_aim:1;
+ u8 rsvd1:5; /* finish byte */
+
+ /* boot device bits */
+ u8 boot_display[2];
+ u8 child_dev_size;
+
+ /* device info */
+ u8 tv_or_lvds_info[33];
+ u8 dev1[33];
+ u8 dev2[33];
+ u8 dev3[33];
+ u8 dev4[33];
+ /* may be another device block here on some platforms */
+};
+
+struct bdb_lvds_options {
+ u8 panel_type;
+ u8 rsvd1;
+ /* LVDS capabilities, stored in a dword */
+ u8 rsvd2:1;
+ u8 lvds_edid:1;
+ u8 pixel_dither:1;
+ u8 pfit_ratio_auto:1;
+ u8 pfit_gfx_mode_enhanced:1;
+ u8 pfit_text_mode_enhanced:1;
+ u8 pfit_mode:2;
+ u8 rsvd4;
+} __attribute__((packed));
+
+/* LFP pointer table contains entries to the struct below */
+struct bdb_lvds_lfp_data_ptr {
+ u16 fp_timing_offset; /* offsets are from start of bdb */
+ u8 fp_table_size;
+ u16 dvo_timing_offset;
+ u8 dvo_table_size;
+ u16 panel_pnp_id_offset;
+ u8 pnp_table_size;
+} __attribute__((packed));
+
+struct bdb_lvds_lfp_data_ptrs {
+ u8 lvds_entries; /* followed by one or more lvds_data_ptr structs */
+ struct bdb_lvds_lfp_data_ptr ptr[16];
+} __attribute__((packed));
+
+/* LFP data has 3 blocks per entry */
+struct lvds_fp_timing {
+ u16 x_res;
+ u16 y_res;
+ u32 lvds_reg;
+ u32 lvds_reg_val;
+ u32 pp_on_reg;
+ u32 pp_on_reg_val;
+ u32 pp_off_reg;
+ u32 pp_off_reg_val;
+ u32 pp_cycle_reg;
+ u32 pp_cycle_reg_val;
+ u32 pfit_reg;
+ u32 pfit_reg_val;
+ u16 terminator;
+} __attribute__((packed));
+
+struct lvds_dvo_timing {
+ u16 clock; /**< In 10khz */
+ u8 hactive_lo;
+ u8 hblank_lo;
+ u8 hblank_hi:4;
+ u8 hactive_hi:4;
+ u8 vactive_lo;
+ u8 vblank_lo;
+ u8 vblank_hi:4;
+ u8 vactive_hi:4;
+ u8 hsync_off_lo;
+ u8 hsync_pulse_width;
+ u8 vsync_pulse_width:4;
+ u8 vsync_off:4;
+ u8 rsvd0:6;
+ u8 hsync_off_hi:2;
+ u8 h_image;
+ u8 v_image;
+ u8 max_hv;
+ u8 h_border;
+ u8 v_border;
+ u8 rsvd1:3;
+ u8 digital:2;
+ u8 vsync_positive:1;
+ u8 hsync_positive:1;
+ u8 rsvd2:1;
+} __attribute__((packed));
+
+struct lvds_pnp_id {
+ u16 mfg_name;
+ u16 product_code;
+ u32 serial;
+ u8 mfg_week;
+ u8 mfg_year;
+} __attribute__((packed));
+
+struct bdb_lvds_lfp_data_entry {
+ struct lvds_fp_timing fp_timing;
+ struct lvds_dvo_timing dvo_timing;
+ struct lvds_pnp_id pnp_id;
+} __attribute__((packed));
+
+struct bdb_lvds_lfp_data {
+ struct bdb_lvds_lfp_data_entry data[16];
+} __attribute__((packed));
+
+struct aimdb_header {
+ char signature[16];
+ char oem_device[20];
+ u16 aimdb_version;
+ u16 aimdb_header_size;
+ u16 aimdb_size;
+} __attribute__((packed));
+
+struct aimdb_block {
+ u8 aimdb_id;
+ u16 aimdb_size;
+} __attribute__((packed));
+
+struct vch_panel_data {
+ u16 fp_timing_offset;
+ u8 fp_timing_size;
+ u16 dvo_timing_offset;
+ u8 dvo_timing_size;
+ u16 text_fitting_offset;
+ u8 text_fitting_size;
+ u16 graphics_fitting_offset;
+ u8 graphics_fitting_size;
+} __attribute__((packed));
+
+struct vch_bdb_22 {
+ struct aimdb_block aimdb_block;
+ struct vch_panel_data panels[16];
+} __attribute__((packed));
+
+bool intel_init_bios(struct drm_device *dev);
+
+/*
+ * Driver<->VBIOS interaction occurs through scratch bits in
+ * GR18 & SWF*.
+ */
+
+/* GR18 bits are set on display switch and hotkey events */
+#define GR18_DRIVER_SWITCH_EN (1<<7) /* 0: VBIOS control, 1: driver control */
+#define GR18_HOTKEY_MASK 0x78 /* See also SWF4 15:0 */
+#define GR18_HK_NONE (0x0<<3)
+#define GR18_HK_LFP_STRETCH (0x1<<3)
+#define GR18_HK_TOGGLE_DISP (0x2<<3)
+#define GR18_HK_DISP_SWITCH (0x4<<3) /* see SWF14 15:0 for what to enable */
+#define GR18_HK_POPUP_DISABLED (0x6<<3)
+#define GR18_HK_POPUP_ENABLED (0x7<<3)
+#define GR18_HK_PFIT (0x8<<3)
+#define GR18_HK_APM_CHANGE (0xa<<3)
+#define GR18_HK_MULTIPLE (0xc<<3)
+#define GR18_USER_INT_EN (1<<2)
+#define GR18_A0000_FLUSH_EN (1<<1)
+#define GR18_SMM_EN (1<<0)
+
+/* Set by driver, cleared by VBIOS */
+#define SWF00_YRES_SHIFT 16
+#define SWF00_XRES_SHIFT 0
+#define SWF00_RES_MASK 0xffff
+
+/* Set by VBIOS at boot time and driver at runtime */
+#define SWF01_TV2_FORMAT_SHIFT 8
+#define SWF01_TV1_FORMAT_SHIFT 0
+#define SWF01_TV_FORMAT_MASK 0xffff
+
+#define SWF10_VBIOS_BLC_I2C_EN (1<<29)
+#define SWF10_GTT_OVERRIDE_EN (1<<28)
+#define SWF10_LFP_DPMS_OVR (1<<27) /* override DPMS on display switch */
+#define SWF10_ACTIVE_TOGGLE_LIST_MASK (7<<24)
+#define SWF10_OLD_TOGGLE 0x0
+#define SWF10_TOGGLE_LIST_1 0x1
+#define SWF10_TOGGLE_LIST_2 0x2
+#define SWF10_TOGGLE_LIST_3 0x3
+#define SWF10_TOGGLE_LIST_4 0x4
+#define SWF10_PANNING_EN (1<<23)
+#define SWF10_DRIVER_LOADED (1<<22)
+#define SWF10_EXTENDED_DESKTOP (1<<21)
+#define SWF10_EXCLUSIVE_MODE (1<<20)
+#define SWF10_OVERLAY_EN (1<<19)
+#define SWF10_PLANEB_HOLDOFF (1<<18)
+#define SWF10_PLANEA_HOLDOFF (1<<17)
+#define SWF10_VGA_HOLDOFF (1<<16)
+#define SWF10_ACTIVE_DISP_MASK 0xffff
+#define SWF10_PIPEB_LFP2 (1<<15)
+#define SWF10_PIPEB_EFP2 (1<<14)
+#define SWF10_PIPEB_TV2 (1<<13)
+#define SWF10_PIPEB_CRT2 (1<<12)
+#define SWF10_PIPEB_LFP (1<<11)
+#define SWF10_PIPEB_EFP (1<<10)
+#define SWF10_PIPEB_TV (1<<9)
+#define SWF10_PIPEB_CRT (1<<8)
+#define SWF10_PIPEA_LFP2 (1<<7)
+#define SWF10_PIPEA_EFP2 (1<<6)
+#define SWF10_PIPEA_TV2 (1<<5)
+#define SWF10_PIPEA_CRT2 (1<<4)
+#define SWF10_PIPEA_LFP (1<<3)
+#define SWF10_PIPEA_EFP (1<<2)
+#define SWF10_PIPEA_TV (1<<1)
+#define SWF10_PIPEA_CRT (1<<0)
+
+#define SWF11_MEMORY_SIZE_SHIFT 16
+#define SWF11_SV_TEST_EN (1<<15)
+#define SWF11_IS_AGP (1<<14)
+#define SWF11_DISPLAY_HOLDOFF (1<<13)
+#define SWF11_DPMS_REDUCED (1<<12)
+#define SWF11_IS_VBE_MODE (1<<11)
+#define SWF11_PIPEB_ACCESS (1<<10) /* 0 here means pipe a */
+#define SWF11_DPMS_MASK 0x07
+#define SWF11_DPMS_OFF (1<<2)
+#define SWF11_DPMS_SUSPEND (1<<1)
+#define SWF11_DPMS_STANDBY (1<<0)
+#define SWF11_DPMS_ON 0
+
+#define SWF14_GFX_PFIT_EN (1<<31)
+#define SWF14_TEXT_PFIT_EN (1<<30)
+#define SWF14_LID_STATUS_CLOSED (1<<29) /* 0 here means open */
+#define SWF14_POPUP_EN (1<<28)
+#define SWF14_DISPLAY_HOLDOFF (1<<27)
+#define SWF14_DISP_DETECT_EN (1<<26)
+#define SWF14_DOCKING_STATUS_DOCKED (1<<25) /* 0 here means undocked */
+#define SWF14_DRIVER_STATUS (1<<24)
+#define SWF14_OS_TYPE_WIN9X (1<<23)
+#define SWF14_OS_TYPE_WINNT (1<<22)
+/* 21:19 rsvd */
+#define SWF14_PM_TYPE_MASK 0x00070000
+#define SWF14_PM_ACPI_VIDEO (0x4 << 16)
+#define SWF14_PM_ACPI (0x3 << 16)
+#define SWF14_PM_APM_12 (0x2 << 16)
+#define SWF14_PM_APM_11 (0x1 << 16)
+#define SWF14_HK_REQUEST_MASK 0x0000ffff /* see GR18 6:3 for event type */
+ /* if GR18 indicates a display switch */
+#define SWF14_DS_PIPEB_LFP2_EN (1<<15)
+#define SWF14_DS_PIPEB_EFP2_EN (1<<14)
+#define SWF14_DS_PIPEB_TV2_EN (1<<13)
+#define SWF14_DS_PIPEB_CRT2_EN (1<<12)
+#define SWF14_DS_PIPEB_LFP_EN (1<<11)
+#define SWF14_DS_PIPEB_EFP_EN (1<<10)
+#define SWF14_DS_PIPEB_TV_EN (1<<9)
+#define SWF14_DS_PIPEB_CRT_EN (1<<8)
+#define SWF14_DS_PIPEA_LFP2_EN (1<<7)
+#define SWF14_DS_PIPEA_EFP2_EN (1<<6)
+#define SWF14_DS_PIPEA_TV2_EN (1<<5)
+#define SWF14_DS_PIPEA_CRT2_EN (1<<4)
+#define SWF14_DS_PIPEA_LFP_EN (1<<3)
+#define SWF14_DS_PIPEA_EFP_EN (1<<2)
+#define SWF14_DS_PIPEA_TV_EN (1<<1)
+#define SWF14_DS_PIPEA_CRT_EN (1<<0)
+ /* if GR18 indicates a panel fitting request */
+#define SWF14_PFIT_EN (1<<0) /* 0 means disable */
+ /* if GR18 indicates an APM change request */
+#define SWF14_APM_HIBERNATE 0x4
+#define SWF14_APM_SUSPEND 0x3
+#define SWF14_APM_STANDBY 0x1
+#define SWF14_APM_RESTORE 0x0
+
+#endif /* _I830_BIOS_H_ */
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
new file mode 100644
index 000000000000..dcaed3466e83
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 temp;
+
+ temp = I915_READ(ADPA);
+ temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
+ temp &= ~ADPA_DAC_ENABLE;
+
+ switch(mode) {
+ case DRM_MODE_DPMS_ON:
+ temp |= ADPA_DAC_ENABLE;
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
+ break;
+ case DRM_MODE_DPMS_SUSPEND:
+ temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
+ break;
+ case DRM_MODE_DPMS_OFF:
+ temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
+ break;
+ }
+
+ I915_WRITE(ADPA, temp);
+}
+
+static int intel_crt_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ if (mode->clock > 400000 || mode->clock < 25000)
+ return MODE_CLOCK_RANGE;
+
+ return MODE_OK;
+}
+
+static bool intel_crt_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void intel_crt_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+
+ struct drm_device *dev = encoder->dev;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int dpll_md_reg;
+ u32 adpa, dpll_md;
+
+ if (intel_crtc->pipe == 0)
+ dpll_md_reg = DPLL_A_MD;
+ else
+ dpll_md_reg = DPLL_B_MD;
+
+ /*
+ * Disable separate mode multiplier used when cloning SDVO to CRT
+ * XXX this needs to be adjusted when we really are cloning
+ */
+ if (IS_I965G(dev)) {
+ dpll_md = I915_READ(dpll_md_reg);
+ I915_WRITE(dpll_md_reg,
+ dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
+ }
+
+ adpa = 0;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ adpa |= ADPA_HSYNC_ACTIVE_HIGH;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ adpa |= ADPA_VSYNC_ACTIVE_HIGH;
+
+ if (intel_crtc->pipe == 0)
+ adpa |= ADPA_PIPE_A_SELECT;
+ else
+ adpa |= ADPA_PIPE_B_SELECT;
+
+ I915_WRITE(ADPA, adpa);
+}
+
+/**
+ * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
+ *
+ * Not for i915G/i915GM
+ *
+ * \return true if CRT is connected.
+ * \return false if CRT is disconnected.
+ */
+static bool intel_crt_detect_hotplug(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 temp;
+
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ temp = I915_READ(PORT_HOTPLUG_EN);
+
+ I915_WRITE(PORT_HOTPLUG_EN,
+ temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5));
+
+ do {
+ if (!(I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT))
+ break;
+ msleep(1);
+ } while (time_after(timeout, jiffies));
+
+ if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) ==
+ CRT_HOTPLUG_MONITOR_COLOR)
+ return true;
+
+ return false;
+}
+
+static bool intel_crt_detect_ddc(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ /* CRT should always be at 0, but check anyway */
+ if (intel_output->type != INTEL_OUTPUT_ANALOG)
+ return false;
+
+ return intel_ddc_probe(intel_output);
+}
+
+static enum drm_connector_status intel_crt_detect(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+
+ if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
+ if (intel_crt_detect_hotplug(connector))
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+ }
+
+ if (intel_crt_detect_ddc(connector))
+ return connector_status_connected;
+
+ /* TODO use load detect */
+ return connector_status_unknown;
+}
+
+static void intel_crt_destroy(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+static int intel_crt_get_modes(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ return intel_ddc_get_modes(intel_output);
+}
+
+static int intel_crt_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t value)
+{
+ struct drm_device *dev = connector->dev;
+
+ if (property == dev->mode_config.dpms_property && connector->encoder)
+ intel_crt_dpms(connector->encoder, (uint32_t)(value & 0xf));
+
+ return 0;
+}
+
+/*
+ * Routines for controlling stuff on the analog port
+ */
+
+static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = {
+ .dpms = intel_crt_dpms,
+ .mode_fixup = intel_crt_mode_fixup,
+ .prepare = intel_encoder_prepare,
+ .commit = intel_encoder_commit,
+ .mode_set = intel_crt_mode_set,
+};
+
+static const struct drm_connector_funcs intel_crt_connector_funcs = {
+ .detect = intel_crt_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = intel_crt_destroy,
+ .set_property = intel_crt_set_property,
+};
+
+static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = {
+ .mode_valid = intel_crt_mode_valid,
+ .get_modes = intel_crt_get_modes,
+ .best_encoder = intel_best_encoder,
+};
+
+static void intel_crt_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_crt_enc_funcs = {
+ .destroy = intel_crt_enc_destroy,
+};
+
+void intel_crt_init(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+ struct intel_output *intel_output;
+
+ intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output)
+ return;
+
+ connector = &intel_output->base;
+ drm_connector_init(dev, &intel_output->base,
+ &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA);
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_crt_enc_funcs,
+ DRM_MODE_ENCODER_DAC);
+
+ drm_mode_connector_attach_encoder(&intel_output->base,
+ &intel_output->enc);
+
+ /* Set up the DDC bus. */
+ intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
+ if (!intel_output->ddc_bus) {
+ dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
+ "failed.\n");
+ return;
+ }
+
+ intel_output->type = INTEL_OUTPUT_ANALOG;
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+
+ drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs);
+ drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
+
+ drm_sysfs_connector_add(connector);
+}
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
new file mode 100644
index 000000000000..e5c1c80d1f90
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -0,0 +1,1618 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+#include "drm_crtc_helper.h"
+
+bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
+
+typedef struct {
+ /* given values */
+ int n;
+ int m1, m2;
+ int p1, p2;
+ /* derived values */
+ int dot;
+ int vco;
+ int m;
+ int p;
+} intel_clock_t;
+
+typedef struct {
+ int min, max;
+} intel_range_t;
+
+typedef struct {
+ int dot_limit;
+ int p2_slow, p2_fast;
+} intel_p2_t;
+
+#define INTEL_P2_NUM 2
+
+typedef struct {
+ intel_range_t dot, vco, n, m, m1, m2, p, p1;
+ intel_p2_t p2;
+} intel_limit_t;
+
+#define I8XX_DOT_MIN 25000
+#define I8XX_DOT_MAX 350000
+#define I8XX_VCO_MIN 930000
+#define I8XX_VCO_MAX 1400000
+#define I8XX_N_MIN 3
+#define I8XX_N_MAX 16
+#define I8XX_M_MIN 96
+#define I8XX_M_MAX 140
+#define I8XX_M1_MIN 18
+#define I8XX_M1_MAX 26
+#define I8XX_M2_MIN 6
+#define I8XX_M2_MAX 16
+#define I8XX_P_MIN 4
+#define I8XX_P_MAX 128
+#define I8XX_P1_MIN 2
+#define I8XX_P1_MAX 33
+#define I8XX_P1_LVDS_MIN 1
+#define I8XX_P1_LVDS_MAX 6
+#define I8XX_P2_SLOW 4
+#define I8XX_P2_FAST 2
+#define I8XX_P2_LVDS_SLOW 14
+#define I8XX_P2_LVDS_FAST 14 /* No fast option */
+#define I8XX_P2_SLOW_LIMIT 165000
+
+#define I9XX_DOT_MIN 20000
+#define I9XX_DOT_MAX 400000
+#define I9XX_VCO_MIN 1400000
+#define I9XX_VCO_MAX 2800000
+#define I9XX_N_MIN 3
+#define I9XX_N_MAX 8
+#define I9XX_M_MIN 70
+#define I9XX_M_MAX 120
+#define I9XX_M1_MIN 10
+#define I9XX_M1_MAX 20
+#define I9XX_M2_MIN 5
+#define I9XX_M2_MAX 9
+#define I9XX_P_SDVO_DAC_MIN 5
+#define I9XX_P_SDVO_DAC_MAX 80
+#define I9XX_P_LVDS_MIN 7
+#define I9XX_P_LVDS_MAX 98
+#define I9XX_P1_MIN 1
+#define I9XX_P1_MAX 8
+#define I9XX_P2_SDVO_DAC_SLOW 10
+#define I9XX_P2_SDVO_DAC_FAST 5
+#define I9XX_P2_SDVO_DAC_SLOW_LIMIT 200000
+#define I9XX_P2_LVDS_SLOW 14
+#define I9XX_P2_LVDS_FAST 7
+#define I9XX_P2_LVDS_SLOW_LIMIT 112000
+
+#define INTEL_LIMIT_I8XX_DVO_DAC 0
+#define INTEL_LIMIT_I8XX_LVDS 1
+#define INTEL_LIMIT_I9XX_SDVO_DAC 2
+#define INTEL_LIMIT_I9XX_LVDS 3
+
+static const intel_limit_t intel_limits[] = {
+ { /* INTEL_LIMIT_I8XX_DVO_DAC */
+ .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
+ .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX },
+ .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX },
+ .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX },
+ .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX },
+ .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX },
+ .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX },
+ .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX },
+ .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+ .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST },
+ },
+ { /* INTEL_LIMIT_I8XX_LVDS */
+ .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
+ .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX },
+ .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX },
+ .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX },
+ .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX },
+ .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX },
+ .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX },
+ .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX },
+ .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+ .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST },
+ },
+ { /* INTEL_LIMIT_I9XX_SDVO_DAC */
+ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
+ .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX },
+ .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX },
+ .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX },
+ .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX },
+ .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX },
+ .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+ .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+ },
+ { /* INTEL_LIMIT_I9XX_LVDS */
+ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
+ .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX },
+ .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX },
+ .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX },
+ .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX },
+ .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX },
+ .p = { .min = I9XX_P_LVDS_MIN, .max = I9XX_P_LVDS_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+ /* The single-channel range is 25-112Mhz, and dual-channel
+ * is 80-224Mhz. Prefer single channel as much as possible.
+ */
+ .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST },
+ },
+};
+
+static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ const intel_limit_t *limit;
+
+ if (IS_I9XX(dev)) {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS];
+ else
+ limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC];
+ } else {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS];
+ else
+ limit = &intel_limits[INTEL_LIMIT_I8XX_DVO_DAC];
+ }
+ return limit;
+}
+
+/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
+
+static void i8xx_clock(int refclk, intel_clock_t *clock)
+{
+ clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+ clock->p = clock->p1 * clock->p2;
+ clock->vco = refclk * clock->m / (clock->n + 2);
+ clock->dot = clock->vco / clock->p;
+}
+
+/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */
+
+static void i9xx_clock(int refclk, intel_clock_t *clock)
+{
+ clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+ clock->p = clock->p1 * clock->p2;
+ clock->vco = refclk * clock->m / (clock->n + 2);
+ clock->dot = clock->vco / clock->p;
+}
+
+static void intel_clock(struct drm_device *dev, int refclk,
+ intel_clock_t *clock)
+{
+ if (IS_I9XX(dev))
+ i9xx_clock (refclk, clock);
+ else
+ i8xx_clock (refclk, clock);
+}
+
+/**
+ * Returns whether any output on the specified pipe is of the specified type
+ */
+bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_connector *l_entry;
+
+ list_for_each_entry(l_entry, &mode_config->connector_list, head) {
+ if (l_entry->encoder &&
+ l_entry->encoder->crtc == crtc) {
+ struct intel_output *intel_output = to_intel_output(l_entry);
+ if (intel_output->type == type)
+ return true;
+ }
+ }
+ return false;
+}
+
+#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; }
+/**
+ * Returns whether the given set of divisors are valid for a given refclk with
+ * the given connectors.
+ */
+
+static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
+{
+ const intel_limit_t *limit = intel_limit (crtc);
+
+ if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1)
+ INTELPllInvalid ("p1 out of range\n");
+ if (clock->p < limit->p.min || limit->p.max < clock->p)
+ INTELPllInvalid ("p out of range\n");
+ if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2)
+ INTELPllInvalid ("m2 out of range\n");
+ if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1)
+ INTELPllInvalid ("m1 out of range\n");
+ if (clock->m1 <= clock->m2)
+ INTELPllInvalid ("m1 <= m2\n");
+ if (clock->m < limit->m.min || limit->m.max < clock->m)
+ INTELPllInvalid ("m out of range\n");
+ if (clock->n < limit->n.min || limit->n.max < clock->n)
+ INTELPllInvalid ("n out of range\n");
+ if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
+ INTELPllInvalid ("vco out of range\n");
+ /* XXX: We may need to be checking "Dot clock" depending on the multiplier,
+ * connector, etc., rather than just a single range.
+ */
+ if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
+ INTELPllInvalid ("dot out of range\n");
+
+ return true;
+}
+
+/**
+ * Returns a set of divisors for the desired target clock with the given
+ * refclk, or FALSE. The returned values represent the clock equation:
+ * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
+ */
+static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
+ int refclk, intel_clock_t *best_clock)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ intel_clock_t clock;
+ const intel_limit_t *limit = intel_limit(crtc);
+ int err = target;
+
+ if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
+ (I915_READ(LVDS) & LVDS_PORT_EN) != 0) {
+ /*
+ * For LVDS, if the panel is on, just rely on its current
+ * settings for dual-channel. We haven't figured out how to
+ * reliably set up different single/dual channel state, if we
+ * even can.
+ */
+ if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+ LVDS_CLKB_POWER_UP)
+ clock.p2 = limit->p2.p2_fast;
+ else
+ clock.p2 = limit->p2.p2_slow;
+ } else {
+ if (target < limit->p2.dot_limit)
+ clock.p2 = limit->p2.p2_slow;
+ else
+ clock.p2 = limit->p2.p2_fast;
+ }
+
+ memset (best_clock, 0, sizeof (*best_clock));
+
+ for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
+ for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 &&
+ clock.m2 <= limit->m2.max; clock.m2++) {
+ for (clock.n = limit->n.min; clock.n <= limit->n.max;
+ clock.n++) {
+ for (clock.p1 = limit->p1.min;
+ clock.p1 <= limit->p1.max; clock.p1++) {
+ int this_err;
+
+ intel_clock(dev, refclk, &clock);
+
+ if (!intel_PLL_is_valid(crtc, &clock))
+ continue;
+
+ this_err = abs(clock.dot - target);
+ if (this_err < err) {
+ *best_clock = clock;
+ err = this_err;
+ }
+ }
+ }
+ }
+ }
+
+ return (err != target);
+}
+
+void
+intel_wait_for_vblank(struct drm_device *dev)
+{
+ /* Wait for 20ms, i.e. one cycle at 50hz. */
+ udelay(20000);
+}
+
+static void
+intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_framebuffer *intel_fb;
+ struct drm_i915_gem_object *obj_priv;
+ struct drm_gem_object *obj;
+ int pipe = intel_crtc->pipe;
+ unsigned long Start, Offset;
+ int dspbase = (pipe == 0 ? DSPAADDR : DSPBADDR);
+ int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF);
+ int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
+ int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+ u32 dspcntr, alignment;
+
+ /* no fb bound */
+ if (!crtc->fb) {
+ DRM_DEBUG("No FB bound\n");
+ return;
+ }
+
+ intel_fb = to_intel_framebuffer(crtc->fb);
+ obj = intel_fb->obj;
+ obj_priv = obj->driver_private;
+
+ switch (obj_priv->tiling_mode) {
+ case I915_TILING_NONE:
+ alignment = 64 * 1024;
+ break;
+ case I915_TILING_X:
+ if (IS_I9XX(dev))
+ alignment = 1024 * 1024;
+ else
+ alignment = 512 * 1024;
+ break;
+ case I915_TILING_Y:
+ /* FIXME: Is this true? */
+ DRM_ERROR("Y tiled not allowed for scan out buffers\n");
+ return;
+ default:
+ BUG();
+ }
+
+ if (i915_gem_object_pin(intel_fb->obj, alignment))
+ return;
+
+ i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1);
+
+ Start = obj_priv->gtt_offset;
+ Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8);
+
+ I915_WRITE(dspstride, crtc->fb->pitch);
+
+ dspcntr = I915_READ(dspcntr_reg);
+ switch (crtc->fb->bits_per_pixel) {
+ case 8:
+ dspcntr |= DISPPLANE_8BPP;
+ break;
+ case 16:
+ if (crtc->fb->depth == 15)
+ dspcntr |= DISPPLANE_15_16BPP;
+ else
+ dspcntr |= DISPPLANE_16BPP;
+ break;
+ case 24:
+ case 32:
+ dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+ break;
+ default:
+ DRM_ERROR("Unknown color depth\n");
+ return;
+ }
+ I915_WRITE(dspcntr_reg, dspcntr);
+
+ DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y);
+ if (IS_I965G(dev)) {
+ I915_WRITE(dspbase, Offset);
+ I915_READ(dspbase);
+ I915_WRITE(dspsurf, Start);
+ I915_READ(dspsurf);
+ } else {
+ I915_WRITE(dspbase, Start + Offset);
+ I915_READ(dspbase);
+ }
+
+ intel_wait_for_vblank(dev);
+
+ if (old_fb) {
+ intel_fb = to_intel_framebuffer(old_fb);
+ i915_gem_object_unpin(intel_fb->obj);
+ }
+
+ if (!dev->primary->master)
+ return;
+
+ master_priv = dev->primary->master->driver_priv;
+ if (!master_priv->sarea_priv)
+ return;
+
+ switch (pipe) {
+ case 0:
+ master_priv->sarea_priv->pipeA_x = x;
+ master_priv->sarea_priv->pipeA_y = y;
+ break;
+ case 1:
+ master_priv->sarea_priv->pipeB_x = x;
+ master_priv->sarea_priv->pipeB_y = y;
+ break;
+ default:
+ DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
+ break;
+ }
+}
+
+
+
+/**
+ * Sets the power management mode of the pipe and plane.
+ *
+ * This code should probably grow support for turning the cursor off and back
+ * on appropriately at the same time as we're turning the pipe off/on.
+ */
+static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_master_private *master_priv;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+ int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+ int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR;
+ int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+ u32 temp;
+ bool enabled;
+
+ /* XXX: When our outputs are all unaware of DPMS modes other than off
+ * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
+ */
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ /* Enable the DPLL */
+ temp = I915_READ(dpll_reg);
+ if ((temp & DPLL_VCO_ENABLE) == 0) {
+ I915_WRITE(dpll_reg, temp);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+ I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+ I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+ }
+
+ /* Enable the pipe */
+ temp = I915_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) == 0)
+ I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
+
+ /* Enable the plane */
+ temp = I915_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+ I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ }
+
+ intel_crtc_load_lut(crtc);
+
+ /* Give the overlay scaler a chance to enable if it's on this pipe */
+ //intel_crtc_dpms_video(crtc, true); TODO
+ break;
+ case DRM_MODE_DPMS_OFF:
+ /* Give the overlay scaler a chance to disable if it's on this pipe */
+ //intel_crtc_dpms_video(crtc, FALSE); TODO
+
+ /* Disable the VGA plane that we never use */
+ I915_WRITE(VGACNTRL, VGA_DISP_DISABLE);
+
+ /* Disable display plane */
+ temp = I915_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+ I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ I915_READ(dspbase_reg);
+ }
+
+ if (!IS_I9XX(dev)) {
+ /* Wait for vblank for the disable to take effect */
+ intel_wait_for_vblank(dev);
+ }
+
+ /* Next, disable display pipes */
+ temp = I915_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) != 0) {
+ I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
+ I915_READ(pipeconf_reg);
+ }
+
+ /* Wait for vblank for the disable to take effect. */
+ intel_wait_for_vblank(dev);
+
+ temp = I915_READ(dpll_reg);
+ if ((temp & DPLL_VCO_ENABLE) != 0) {
+ I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ }
+
+ /* Wait for the clocks to turn off. */
+ udelay(150);
+ break;
+ }
+
+ if (!dev->primary->master)
+ return;
+
+ master_priv = dev->primary->master->driver_priv;
+ if (!master_priv->sarea_priv)
+ return;
+
+ enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
+
+ switch (pipe) {
+ case 0:
+ master_priv->sarea_priv->pipeA_w = enabled ? crtc->mode.hdisplay : 0;
+ master_priv->sarea_priv->pipeA_h = enabled ? crtc->mode.vdisplay : 0;
+ break;
+ case 1:
+ master_priv->sarea_priv->pipeB_w = enabled ? crtc->mode.hdisplay : 0;
+ master_priv->sarea_priv->pipeB_h = enabled ? crtc->mode.vdisplay : 0;
+ break;
+ default:
+ DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
+ break;
+ }
+
+ intel_crtc->dpms_mode = mode;
+}
+
+static void intel_crtc_prepare (struct drm_crtc *crtc)
+{
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void intel_crtc_commit (struct drm_crtc *crtc)
+{
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+void intel_encoder_prepare (struct drm_encoder *encoder)
+{
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ /* lvds has its own version of prepare see intel_lvds_prepare */
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+void intel_encoder_commit (struct drm_encoder *encoder)
+{
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ /* lvds has its own version of commit see intel_lvds_commit */
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+
+/** Returns the core display clock speed for i830 - i945 */
+static int intel_get_core_clock_speed(struct drm_device *dev)
+{
+
+ /* Core clock values taken from the published datasheets.
+ * The 830 may go up to 166 Mhz, which we should check.
+ */
+ if (IS_I945G(dev))
+ return 400000;
+ else if (IS_I915G(dev))
+ return 333000;
+ else if (IS_I945GM(dev) || IS_845G(dev))
+ return 200000;
+ else if (IS_I915GM(dev)) {
+ u16 gcfgc = 0;
+
+ pci_read_config_word(dev->pdev, GCFGC, &gcfgc);
+
+ if (gcfgc & GC_LOW_FREQUENCY_ENABLE)
+ return 133000;
+ else {
+ switch (gcfgc & GC_DISPLAY_CLOCK_MASK) {
+ case GC_DISPLAY_CLOCK_333_MHZ:
+ return 333000;
+ default:
+ case GC_DISPLAY_CLOCK_190_200_MHZ:
+ return 190000;
+ }
+ }
+ } else if (IS_I865G(dev))
+ return 266000;
+ else if (IS_I855(dev)) {
+ u16 hpllcc = 0;
+ /* Assume that the hardware is in the high speed state. This
+ * should be the default.
+ */
+ switch (hpllcc & GC_CLOCK_CONTROL_MASK) {
+ case GC_CLOCK_133_200:
+ case GC_CLOCK_100_200:
+ return 200000;
+ case GC_CLOCK_166_250:
+ return 250000;
+ case GC_CLOCK_100_133:
+ return 133000;
+ }
+ } else /* 852, 830 */
+ return 133000;
+
+ return 0; /* Silence gcc warning */
+}
+
+
+/**
+ * Return the pipe currently connected to the panel fitter,
+ * or -1 if the panel fitter is not present or not in use
+ */
+static int intel_panel_fitter_pipe (struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pfit_control;
+
+ /* i830 doesn't have a panel fitter */
+ if (IS_I830(dev))
+ return -1;
+
+ pfit_control = I915_READ(PFIT_CONTROL);
+
+ /* See if the panel fitter is in use */
+ if ((pfit_control & PFIT_ENABLE) == 0)
+ return -1;
+
+ /* 965 can place panel fitter on either pipe */
+ if (IS_I965G(dev))
+ return (pfit_control >> 29) & 0x3;
+
+ /* older chips can only use pipe 1 */
+ return 1;
+}
+
+static void intel_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ int fp_reg = (pipe == 0) ? FPA0 : FPB0;
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+ int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD;
+ int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+ int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+ int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
+ int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
+ int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
+ int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
+ int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
+ int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
+ int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
+ int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
+ int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
+ int refclk;
+ intel_clock_t clock;
+ u32 dpll = 0, fp = 0, dspcntr, pipeconf;
+ bool ok, is_sdvo = false, is_dvo = false;
+ bool is_crt = false, is_lvds = false, is_tv = false;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_connector *connector;
+
+ drm_vblank_pre_modeset(dev, pipe);
+
+ list_for_each_entry(connector, &mode_config->connector_list, head) {
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ if (!connector->encoder || connector->encoder->crtc != crtc)
+ continue;
+
+ switch (intel_output->type) {
+ case INTEL_OUTPUT_LVDS:
+ is_lvds = true;
+ break;
+ case INTEL_OUTPUT_SDVO:
+ is_sdvo = true;
+ break;
+ case INTEL_OUTPUT_DVO:
+ is_dvo = true;
+ break;
+ case INTEL_OUTPUT_TVOUT:
+ is_tv = true;
+ break;
+ case INTEL_OUTPUT_ANALOG:
+ is_crt = true;
+ break;
+ }
+ }
+
+ if (IS_I9XX(dev)) {
+ refclk = 96000;
+ } else {
+ refclk = 48000;
+ }
+
+ ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock);
+ if (!ok) {
+ DRM_ERROR("Couldn't find PLL settings for mode!\n");
+ return;
+ }
+
+ fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
+
+ dpll = DPLL_VGA_MODE_DIS;
+ if (IS_I9XX(dev)) {
+ if (is_lvds)
+ dpll |= DPLLB_MODE_LVDS;
+ else
+ dpll |= DPLLB_MODE_DAC_SERIAL;
+ if (is_sdvo) {
+ dpll |= DPLL_DVO_HIGH_SPEED;
+ if (IS_I945G(dev) || IS_I945GM(dev)) {
+ int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
+ }
+ }
+
+ /* compute bitmask from p1 value */
+ dpll |= (1 << (clock.p1 - 1)) << 16;
+ switch (clock.p2) {
+ case 5:
+ dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
+ break;
+ case 7:
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7;
+ break;
+ case 10:
+ dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10;
+ break;
+ case 14:
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
+ break;
+ }
+ if (IS_I965G(dev))
+ dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
+ } else {
+ if (is_lvds) {
+ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+ } else {
+ if (clock.p1 == 2)
+ dpll |= PLL_P1_DIVIDE_BY_TWO;
+ else
+ dpll |= (clock.p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+ if (clock.p2 == 4)
+ dpll |= PLL_P2_DIVIDE_BY_4;
+ }
+ }
+
+ if (is_tv) {
+ /* XXX: just matching BIOS for now */
+/* dpll |= PLL_REF_INPUT_TVCLKINBC; */
+ dpll |= 3;
+ }
+ else
+ dpll |= PLL_REF_INPUT_DREFCLK;
+
+ /* setup pipeconf */
+ pipeconf = I915_READ(pipeconf_reg);
+
+ /* Set up the display plane register */
+ dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+ if (pipe == 0)
+ dspcntr |= DISPPLANE_SEL_PIPE_A;
+ else
+ dspcntr |= DISPPLANE_SEL_PIPE_B;
+
+ if (pipe == 0 && !IS_I965G(dev)) {
+ /* Enable pixel doubling when the dot clock is > 90% of the (display)
+ * core speed.
+ *
+ * XXX: No double-wide on 915GM pipe B. Is that the only reason for the
+ * pipe == 0 check?
+ */
+ if (mode->clock > intel_get_core_clock_speed(dev) * 9 / 10)
+ pipeconf |= PIPEACONF_DOUBLE_WIDE;
+ else
+ pipeconf &= ~PIPEACONF_DOUBLE_WIDE;
+ }
+
+ dspcntr |= DISPLAY_PLANE_ENABLE;
+ pipeconf |= PIPEACONF_ENABLE;
+ dpll |= DPLL_VCO_ENABLE;
+
+
+ /* Disable the panel fitter if it was on our pipe */
+ if (intel_panel_fitter_pipe(dev) == pipe)
+ I915_WRITE(PFIT_CONTROL, 0);
+
+ DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
+ drm_mode_debug_printmodeline(mode);
+
+
+ if (dpll & DPLL_VCO_ENABLE) {
+ I915_WRITE(fp_reg, fp);
+ I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ udelay(150);
+ }
+
+ /* The LVDS pin pair needs to be on before the DPLLs are enabled.
+ * This is an exception to the general rule that mode_set doesn't turn
+ * things on.
+ */
+ if (is_lvds) {
+ u32 lvds = I915_READ(LVDS);
+
+ lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
+ /* Set the B0-B3 data pairs corresponding to whether we're going to
+ * set the DPLLs for dual-channel mode or not.
+ */
+ if (clock.p2 == 7)
+ lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
+ else
+ lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
+
+ /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
+ * appropriately here, but we need to look more thoroughly into how
+ * panels behave in the two modes.
+ */
+
+ I915_WRITE(LVDS, lvds);
+ I915_READ(LVDS);
+ }
+
+ I915_WRITE(fp_reg, fp);
+ I915_WRITE(dpll_reg, dpll);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+
+ if (IS_I965G(dev)) {
+ int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
+ ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
+ } else {
+ /* write it again -- the BIOS does, after all */
+ I915_WRITE(dpll_reg, dpll);
+ }
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+
+ I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
+ ((adjusted_mode->crtc_htotal - 1) << 16));
+ I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) |
+ ((adjusted_mode->crtc_hblank_end - 1) << 16));
+ I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) |
+ ((adjusted_mode->crtc_hsync_end - 1) << 16));
+ I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) |
+ ((adjusted_mode->crtc_vtotal - 1) << 16));
+ I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) |
+ ((adjusted_mode->crtc_vblank_end - 1) << 16));
+ I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) |
+ ((adjusted_mode->crtc_vsync_end - 1) << 16));
+ /* pipesrc and dspsize control the size that is scaled from, which should
+ * always be the user's requested size.
+ */
+ I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
+ I915_WRITE(dsppos_reg, 0);
+ I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+ I915_WRITE(pipeconf_reg, pipeconf);
+ I915_READ(pipeconf_reg);
+
+ intel_wait_for_vblank(dev);
+
+ I915_WRITE(dspcntr_reg, dspcntr);
+
+ /* Flush the plane changes */
+ intel_pipe_set_base(crtc, x, y, old_fb);
+
+ drm_vblank_post_modeset(dev, pipe);
+}
+
+/** Loads the palette/gamma unit for the CRTC with the prepared values */
+void intel_crtc_load_lut(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int palreg = (intel_crtc->pipe == 0) ? PALETTE_A : PALETTE_B;
+ int i;
+
+ /* The clocks have to be on to load the palette. */
+ if (!crtc->enabled)
+ return;
+
+ for (i = 0; i < 256; i++) {
+ I915_WRITE(palreg + 4 * i,
+ (intel_crtc->lut_r[i] << 16) |
+ (intel_crtc->lut_g[i] << 8) |
+ intel_crtc->lut_b[i]);
+ }
+}
+
+static int intel_crtc_cursor_set(struct drm_crtc *crtc,
+ struct drm_file *file_priv,
+ uint32_t handle,
+ uint32_t width, uint32_t height)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_gem_object *bo;
+ struct drm_i915_gem_object *obj_priv;
+ int pipe = intel_crtc->pipe;
+ uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR;
+ uint32_t base = (pipe == 0) ? CURABASE : CURBBASE;
+ uint32_t temp;
+ size_t addr;
+
+ DRM_DEBUG("\n");
+
+ /* if we want to turn off the cursor ignore width and height */
+ if (!handle) {
+ DRM_DEBUG("cursor off\n");
+ /* turn of the cursor */
+ temp = 0;
+ temp |= CURSOR_MODE_DISABLE;
+
+ I915_WRITE(control, temp);
+ I915_WRITE(base, 0);
+ return 0;
+ }
+
+ /* Currently we only support 64x64 cursors */
+ if (width != 64 || height != 64) {
+ DRM_ERROR("we currently only support 64x64 cursors\n");
+ return -EINVAL;
+ }
+
+ bo = drm_gem_object_lookup(dev, file_priv, handle);
+ if (!bo)
+ return -ENOENT;
+
+ obj_priv = bo->driver_private;
+
+ if (bo->size < width * height * 4) {
+ DRM_ERROR("buffer is to small\n");
+ drm_gem_object_unreference(bo);
+ return -ENOMEM;
+ }
+
+ if (dev_priv->cursor_needs_physical) {
+ addr = dev->agp->base + obj_priv->gtt_offset;
+ } else {
+ addr = obj_priv->gtt_offset;
+ }
+
+ intel_crtc->cursor_addr = addr;
+ temp = 0;
+ /* set the pipe for the cursor */
+ temp |= (pipe << 28);
+ temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
+
+ I915_WRITE(control, temp);
+ I915_WRITE(base, addr);
+
+ return 0;
+}
+
+static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ uint32_t temp = 0;
+ uint32_t adder;
+
+ if (x < 0) {
+ temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT);
+ x = -x;
+ }
+ if (y < 0) {
+ temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT);
+ y = -y;
+ }
+
+ temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT);
+ temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT);
+
+ adder = intel_crtc->cursor_addr;
+ I915_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp);
+ I915_WRITE((pipe == 0) ? CURABASE : CURBBASE, adder);
+
+ return 0;
+}
+
+/** Sets the color ramps on behalf of RandR */
+void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+ u16 blue, int regno)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ intel_crtc->lut_r[regno] = red >> 8;
+ intel_crtc->lut_g[regno] = green >> 8;
+ intel_crtc->lut_b[regno] = blue >> 8;
+}
+
+static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
+ u16 *blue, uint32_t size)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int i;
+
+ if (size != 256)
+ return;
+
+ for (i = 0; i < 256; i++) {
+ intel_crtc->lut_r[i] = red[i] >> 8;
+ intel_crtc->lut_g[i] = green[i] >> 8;
+ intel_crtc->lut_b[i] = blue[i] >> 8;
+ }
+
+ intel_crtc_load_lut(crtc);
+}
+
+/**
+ * Get a pipe with a simple mode set on it for doing load-based monitor
+ * detection.
+ *
+ * It will be up to the load-detect code to adjust the pipe as appropriate for
+ * its requirements. The pipe will be connected to no other outputs.
+ *
+ * Currently this code will only succeed if there is a pipe with no outputs
+ * configured for it. In the future, it could choose to temporarily disable
+ * some outputs to free up a pipe for its use.
+ *
+ * \return crtc, or NULL if no pipes are available.
+ */
+
+/* VESA 640x480x72Hz mode to set on the pipe */
+static struct drm_display_mode load_detect_mode = {
+ DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 31500, 640, 664,
+ 704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+};
+
+struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
+ struct drm_display_mode *mode,
+ int *dpms_mode)
+{
+ struct intel_crtc *intel_crtc;
+ struct drm_crtc *possible_crtc;
+ struct drm_crtc *supported_crtc =NULL;
+ struct drm_encoder *encoder = &intel_output->enc;
+ struct drm_crtc *crtc = NULL;
+ struct drm_device *dev = encoder->dev;
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ struct drm_crtc_helper_funcs *crtc_funcs;
+ int i = -1;
+
+ /*
+ * Algorithm gets a little messy:
+ * - if the connector already has an assigned crtc, use it (but make
+ * sure it's on first)
+ * - try to find the first unused crtc that can drive this connector,
+ * and use that if we find one
+ * - if there are no unused crtcs available, try to use the first
+ * one we found that supports the connector
+ */
+
+ /* See if we already have a CRTC for this connector */
+ if (encoder->crtc) {
+ crtc = encoder->crtc;
+ /* Make sure the crtc and connector are running */
+ intel_crtc = to_intel_crtc(crtc);
+ *dpms_mode = intel_crtc->dpms_mode;
+ if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
+ crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+ }
+ return crtc;
+ }
+
+ /* Find an unused one (if possible) */
+ list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) {
+ i++;
+ if (!(encoder->possible_crtcs & (1 << i)))
+ continue;
+ if (!possible_crtc->enabled) {
+ crtc = possible_crtc;
+ break;
+ }
+ if (!supported_crtc)
+ supported_crtc = possible_crtc;
+ }
+
+ /*
+ * If we didn't find an unused CRTC, don't use any.
+ */
+ if (!crtc) {
+ return NULL;
+ }
+
+ encoder->crtc = crtc;
+ intel_output->load_detect_temp = true;
+
+ intel_crtc = to_intel_crtc(crtc);
+ *dpms_mode = intel_crtc->dpms_mode;
+
+ if (!crtc->enabled) {
+ if (!mode)
+ mode = &load_detect_mode;
+ drm_crtc_helper_set_mode(crtc, mode, 0, 0, crtc->fb);
+ } else {
+ if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
+ crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+ }
+
+ /* Add this connector to the crtc */
+ encoder_funcs->mode_set(encoder, &crtc->mode, &crtc->mode);
+ encoder_funcs->commit(encoder);
+ }
+ /* let the connector get through one full cycle before testing */
+ intel_wait_for_vblank(dev);
+
+ return crtc;
+}
+
+void intel_release_load_detect_pipe(struct intel_output *intel_output, int dpms_mode)
+{
+ struct drm_encoder *encoder = &intel_output->enc;
+ struct drm_device *dev = encoder->dev;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+
+ if (intel_output->load_detect_temp) {
+ encoder->crtc = NULL;
+ intel_output->load_detect_temp = false;
+ crtc->enabled = drm_helper_crtc_in_use(crtc);
+ drm_helper_disable_unused_functions(dev);
+ }
+
+ /* Switch crtc and output back off if necessary */
+ if (crtc->enabled && dpms_mode != DRM_MODE_DPMS_ON) {
+ if (encoder->crtc == crtc)
+ encoder_funcs->dpms(encoder, dpms_mode);
+ crtc_funcs->dpms(crtc, dpms_mode);
+ }
+}
+
+/* Returns the clock of the currently programmed mode of the given pipe. */
+static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ u32 dpll = I915_READ((pipe == 0) ? DPLL_A : DPLL_B);
+ u32 fp;
+ intel_clock_t clock;
+
+ if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
+ fp = I915_READ((pipe == 0) ? FPA0 : FPB0);
+ else
+ fp = I915_READ((pipe == 0) ? FPA1 : FPB1);
+
+ clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
+ clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
+ clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
+ if (IS_I9XX(dev)) {
+ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT);
+
+ switch (dpll & DPLL_MODE_MASK) {
+ case DPLLB_MODE_DAC_SERIAL:
+ clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ?
+ 5 : 10;
+ break;
+ case DPLLB_MODE_LVDS:
+ clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ?
+ 7 : 14;
+ break;
+ default:
+ DRM_DEBUG("Unknown DPLL mode %08x in programmed "
+ "mode\n", (int)(dpll & DPLL_MODE_MASK));
+ return 0;
+ }
+
+ /* XXX: Handle the 100Mhz refclk */
+ i9xx_clock(96000, &clock);
+ } else {
+ bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN);
+
+ if (is_lvds) {
+ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT);
+ clock.p2 = 14;
+
+ if ((dpll & PLL_REF_INPUT_MASK) ==
+ PLLB_REF_INPUT_SPREADSPECTRUMIN) {
+ /* XXX: might not be 66MHz */
+ i8xx_clock(66000, &clock);
+ } else
+ i8xx_clock(48000, &clock);
+ } else {
+ if (dpll & PLL_P1_DIVIDE_BY_TWO)
+ clock.p1 = 2;
+ else {
+ clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT) + 2;
+ }
+ if (dpll & PLL_P2_DIVIDE_BY_4)
+ clock.p2 = 4;
+ else
+ clock.p2 = 2;
+
+ i8xx_clock(48000, &clock);
+ }
+ }
+
+ /* XXX: It would be nice to validate the clocks, but we can't reuse
+ * i830PllIsValid() because it relies on the xf86_config connector
+ * configuration being accurate, which it isn't necessarily.
+ */
+
+ return clock.dot;
+}
+
+/** Returns the currently programmed mode of the given pipe. */
+struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
+ struct drm_crtc *crtc)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ struct drm_display_mode *mode;
+ int htot = I915_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B);
+ int hsync = I915_READ((pipe == 0) ? HSYNC_A : HSYNC_B);
+ int vtot = I915_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B);
+ int vsync = I915_READ((pipe == 0) ? VSYNC_A : VSYNC_B);
+
+ mode = kzalloc(sizeof(*mode), GFP_KERNEL);
+ if (!mode)
+ return NULL;
+
+ mode->clock = intel_crtc_clock_get(dev, crtc);
+ mode->hdisplay = (htot & 0xffff) + 1;
+ mode->htotal = ((htot & 0xffff0000) >> 16) + 1;
+ mode->hsync_start = (hsync & 0xffff) + 1;
+ mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1;
+ mode->vdisplay = (vtot & 0xffff) + 1;
+ mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1;
+ mode->vsync_start = (vsync & 0xffff) + 1;
+ mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1;
+
+ drm_mode_set_name(mode);
+ drm_mode_set_crtcinfo(mode, 0);
+
+ return mode;
+}
+
+static void intel_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ drm_crtc_cleanup(crtc);
+ kfree(intel_crtc);
+}
+
+static const struct drm_crtc_helper_funcs intel_helper_funcs = {
+ .dpms = intel_crtc_dpms,
+ .mode_fixup = intel_crtc_mode_fixup,
+ .mode_set = intel_crtc_mode_set,
+ .mode_set_base = intel_pipe_set_base,
+ .prepare = intel_crtc_prepare,
+ .commit = intel_crtc_commit,
+};
+
+static const struct drm_crtc_funcs intel_crtc_funcs = {
+ .cursor_set = intel_crtc_cursor_set,
+ .cursor_move = intel_crtc_cursor_move,
+ .gamma_set = intel_crtc_gamma_set,
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = intel_crtc_destroy,
+};
+
+
+static void intel_crtc_init(struct drm_device *dev, int pipe)
+{
+ struct intel_crtc *intel_crtc;
+ int i;
+
+ intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL);
+ if (intel_crtc == NULL)
+ return;
+
+ drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs);
+
+ drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
+ intel_crtc->pipe = pipe;
+ for (i = 0; i < 256; i++) {
+ intel_crtc->lut_r[i] = i;
+ intel_crtc->lut_g[i] = i;
+ intel_crtc->lut_b[i] = i;
+ }
+
+ intel_crtc->cursor_addr = 0;
+ intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF;
+ drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
+
+ intel_crtc->mode_set.crtc = &intel_crtc->base;
+ intel_crtc->mode_set.connectors = (struct drm_connector **)(intel_crtc + 1);
+ intel_crtc->mode_set.num_connectors = 0;
+
+ if (i915_fbpercrtc) {
+
+
+
+ }
+}
+
+struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe)
+{
+ struct drm_crtc *crtc = NULL;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ if (intel_crtc->pipe == pipe)
+ break;
+ }
+ return crtc;
+}
+
+static int intel_connector_clones(struct drm_device *dev, int type_mask)
+{
+ int index_mask = 0;
+ struct drm_connector *connector;
+ int entry = 0;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct intel_output *intel_output = to_intel_output(connector);
+ if (type_mask & (1 << intel_output->type))
+ index_mask |= (1 << entry);
+ entry++;
+ }
+ return index_mask;
+}
+
+
+static void intel_setup_outputs(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+
+ intel_crt_init(dev);
+
+ /* Set up integrated LVDS */
+ if (IS_MOBILE(dev) && !IS_I830(dev))
+ intel_lvds_init(dev);
+
+ if (IS_I9XX(dev)) {
+ intel_sdvo_init(dev, SDVOB);
+ intel_sdvo_init(dev, SDVOC);
+ } else
+ intel_dvo_init(dev);
+
+ if (IS_I9XX(dev) && !IS_I915G(dev))
+ intel_tv_init(dev);
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct drm_encoder *encoder = &intel_output->enc;
+ int crtc_mask = 0, clone_mask = 0;
+
+ /* valid crtcs */
+ switch(intel_output->type) {
+ case INTEL_OUTPUT_DVO:
+ case INTEL_OUTPUT_SDVO:
+ crtc_mask = ((1 << 0)|
+ (1 << 1));
+ clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
+ (1 << INTEL_OUTPUT_DVO) |
+ (1 << INTEL_OUTPUT_SDVO));
+ break;
+ case INTEL_OUTPUT_ANALOG:
+ crtc_mask = ((1 << 0)|
+ (1 << 1));
+ clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
+ (1 << INTEL_OUTPUT_DVO) |
+ (1 << INTEL_OUTPUT_SDVO));
+ break;
+ case INTEL_OUTPUT_LVDS:
+ crtc_mask = (1 << 1);
+ clone_mask = (1 << INTEL_OUTPUT_LVDS);
+ break;
+ case INTEL_OUTPUT_TVOUT:
+ crtc_mask = ((1 << 0) |
+ (1 << 1));
+ clone_mask = (1 << INTEL_OUTPUT_TVOUT);
+ break;
+ }
+ encoder->possible_crtcs = crtc_mask;
+ encoder->possible_clones = intel_connector_clones(dev, clone_mask);
+ }
+}
+
+static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+ struct drm_device *dev = fb->dev;
+
+ if (fb->fbdev)
+ intelfb_remove(dev, fb);
+
+ drm_framebuffer_cleanup(fb);
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(intel_fb->obj);
+ mutex_unlock(&dev->struct_mutex);
+
+ kfree(intel_fb);
+}
+
+static int intel_user_framebuffer_create_handle(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int *handle)
+{
+ struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+ struct drm_gem_object *object = intel_fb->obj;
+
+ return drm_gem_handle_create(file_priv, object, handle);
+}
+
+static const struct drm_framebuffer_funcs intel_fb_funcs = {
+ .destroy = intel_user_framebuffer_destroy,
+ .create_handle = intel_user_framebuffer_create_handle,
+};
+
+int intel_framebuffer_create(struct drm_device *dev,
+ struct drm_mode_fb_cmd *mode_cmd,
+ struct drm_framebuffer **fb,
+ struct drm_gem_object *obj)
+{
+ struct intel_framebuffer *intel_fb;
+ int ret;
+
+ intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
+ if (!intel_fb)
+ return -ENOMEM;
+
+ ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
+ if (ret) {
+ DRM_ERROR("framebuffer init failed %d\n", ret);
+ return ret;
+ }
+
+ drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd);
+
+ intel_fb->obj = obj;
+
+ *fb = &intel_fb->base;
+
+ return 0;
+}
+
+
+static struct drm_framebuffer *
+intel_user_framebuffer_create(struct drm_device *dev,
+ struct drm_file *filp,
+ struct drm_mode_fb_cmd *mode_cmd)
+{
+ struct drm_gem_object *obj;
+ struct drm_framebuffer *fb;
+ int ret;
+
+ obj = drm_gem_object_lookup(dev, filp, mode_cmd->handle);
+ if (!obj)
+ return NULL;
+
+ ret = intel_framebuffer_create(dev, mode_cmd, &fb, obj);
+ if (ret) {
+ drm_gem_object_unreference(obj);
+ return NULL;
+ }
+
+ return fb;
+}
+
+static const struct drm_mode_config_funcs intel_mode_funcs = {
+ .fb_create = intel_user_framebuffer_create,
+ .fb_changed = intelfb_probe,
+};
+
+void intel_modeset_init(struct drm_device *dev)
+{
+ int num_pipe;
+ int i;
+
+ drm_mode_config_init(dev);
+
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+
+ dev->mode_config.funcs = (void *)&intel_mode_funcs;
+
+ if (IS_I965G(dev)) {
+ dev->mode_config.max_width = 8192;
+ dev->mode_config.max_height = 8192;
+ } else {
+ dev->mode_config.max_width = 2048;
+ dev->mode_config.max_height = 2048;
+ }
+
+ /* set memory base */
+ if (IS_I9XX(dev))
+ dev->mode_config.fb_base = pci_resource_start(dev->pdev, 2);
+ else
+ dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0);
+
+ if (IS_MOBILE(dev) || IS_I9XX(dev))
+ num_pipe = 2;
+ else
+ num_pipe = 1;
+ DRM_DEBUG("%d display pipe%s available.\n",
+ num_pipe, num_pipe > 1 ? "s" : "");
+
+ for (i = 0; i < num_pipe; i++) {
+ intel_crtc_init(dev, i);
+ }
+
+ intel_setup_outputs(dev);
+}
+
+void intel_modeset_cleanup(struct drm_device *dev)
+{
+ drm_mode_config_cleanup(dev);
+}
+
+
+/* current intel driver doesn't take advantage of encoders
+ always give back the encoder for the connector
+*/
+struct drm_encoder *intel_best_encoder(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ return &intel_output->enc;
+}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
new file mode 100644
index 000000000000..407edd5bf582
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright (c) 2007-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef __INTEL_DRV_H__
+#define __INTEL_DRV_H__
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include "drm_crtc.h"
+
+#include "drm_crtc_helper.h"
+/*
+ * Display related stuff
+ */
+
+/* store information about an Ixxx DVO */
+/* The i830->i865 use multiple DVOs with multiple i2cs */
+/* the i915, i945 have a single sDVO i2c bus - which is different */
+#define MAX_OUTPUTS 6
+/* maximum connectors per crtcs in the mode set */
+#define INTELFB_CONN_LIMIT 4
+
+#define INTEL_I2C_BUS_DVO 1
+#define INTEL_I2C_BUS_SDVO 2
+
+/* these are outputs from the chip - integrated only
+ external chips are via DVO or SDVO output */
+#define INTEL_OUTPUT_UNUSED 0
+#define INTEL_OUTPUT_ANALOG 1
+#define INTEL_OUTPUT_DVO 2
+#define INTEL_OUTPUT_SDVO 3
+#define INTEL_OUTPUT_LVDS 4
+#define INTEL_OUTPUT_TVOUT 5
+
+#define INTEL_DVO_CHIP_NONE 0
+#define INTEL_DVO_CHIP_LVDS 1
+#define INTEL_DVO_CHIP_TMDS 2
+#define INTEL_DVO_CHIP_TVOUT 4
+
+struct intel_i2c_chan {
+ struct drm_device *drm_dev; /* for getting at dev. private (mmio etc.) */
+ u32 reg; /* GPIO reg */
+ struct i2c_adapter adapter;
+ struct i2c_algo_bit_data algo;
+ u8 slave_addr;
+};
+
+struct intel_framebuffer {
+ struct drm_framebuffer base;
+ struct drm_gem_object *obj;
+};
+
+
+struct intel_output {
+ struct drm_connector base;
+
+ struct drm_encoder enc;
+ int type;
+ struct intel_i2c_chan *i2c_bus; /* for control functions */
+ struct intel_i2c_chan *ddc_bus; /* for DDC only stuff */
+ bool load_detect_temp;
+ void *dev_priv;
+};
+
+struct intel_crtc {
+ struct drm_crtc base;
+ int pipe;
+ int plane;
+ uint32_t cursor_addr;
+ u8 lut_r[256], lut_g[256], lut_b[256];
+ int dpms_mode;
+ struct intel_framebuffer *fbdev_fb;
+ /* a mode_set for fbdev users on this crtc */
+ struct drm_mode_set mode_set;
+};
+
+#define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
+#define to_intel_output(x) container_of(x, struct intel_output, base)
+#define enc_to_intel_output(x) container_of(x, struct intel_output, enc)
+#define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base)
+
+struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg,
+ const char *name);
+void intel_i2c_destroy(struct intel_i2c_chan *chan);
+int intel_ddc_get_modes(struct intel_output *intel_output);
+extern bool intel_ddc_probe(struct intel_output *intel_output);
+
+extern void intel_crt_init(struct drm_device *dev);
+extern void intel_sdvo_init(struct drm_device *dev, int output_device);
+extern void intel_dvo_init(struct drm_device *dev);
+extern void intel_tv_init(struct drm_device *dev);
+extern void intel_lvds_init(struct drm_device *dev);
+
+extern void intel_crtc_load_lut(struct drm_crtc *crtc);
+extern void intel_encoder_prepare (struct drm_encoder *encoder);
+extern void intel_encoder_commit (struct drm_encoder *encoder);
+
+extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
+
+extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
+ struct drm_crtc *crtc);
+extern void intel_wait_for_vblank(struct drm_device *dev);
+extern struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe);
+extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
+ struct drm_display_mode *mode,
+ int *dpms_mode);
+extern void intel_release_load_detect_pipe(struct intel_output *intel_output,
+ int dpms_mode);
+
+extern struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB);
+extern int intel_sdvo_supports_hotplug(struct drm_connector *connector);
+extern void intel_sdvo_set_hotplug(struct drm_connector *connector, int enable);
+extern int intelfb_probe(struct drm_device *dev);
+extern int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb);
+extern int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc);
+extern void intelfb_restore(void);
+extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+ u16 blue, int regno);
+
+extern int intel_framebuffer_create(struct drm_device *dev,
+ struct drm_mode_fb_cmd *mode_cmd,
+ struct drm_framebuffer **fb,
+ struct drm_gem_object *obj);
+#endif /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
new file mode 100644
index 000000000000..8b8d6e65cd3f
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -0,0 +1,495 @@
+/*
+ * Copyright 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "dvo.h"
+
+#define SIL164_ADDR 0x38
+#define CH7xxx_ADDR 0x76
+#define TFP410_ADDR 0x38
+
+static struct intel_dvo_device intel_dvo_devices[] = {
+ {
+ .type = INTEL_DVO_CHIP_TMDS,
+ .name = "sil164",
+ .dvo_reg = DVOC,
+ .slave_addr = SIL164_ADDR,
+ .dev_ops = &sil164_ops,
+ },
+ {
+ .type = INTEL_DVO_CHIP_TMDS,
+ .name = "ch7xxx",
+ .dvo_reg = DVOC,
+ .slave_addr = CH7xxx_ADDR,
+ .dev_ops = &ch7xxx_ops,
+ },
+ {
+ .type = INTEL_DVO_CHIP_LVDS,
+ .name = "ivch",
+ .dvo_reg = DVOA,
+ .slave_addr = 0x02, /* Might also be 0x44, 0x84, 0xc4 */
+ .dev_ops = &ivch_ops,
+ },
+ {
+ .type = INTEL_DVO_CHIP_TMDS,
+ .name = "tfp410",
+ .dvo_reg = DVOC,
+ .slave_addr = TFP410_ADDR,
+ .dev_ops = &tfp410_ops,
+ },
+ {
+ .type = INTEL_DVO_CHIP_LVDS,
+ .name = "ch7017",
+ .dvo_reg = DVOC,
+ .slave_addr = 0x75,
+ .gpio = GPIOE,
+ .dev_ops = &ch7017_ops,
+ }
+};
+
+static void intel_dvo_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_i915_private *dev_priv = encoder->dev->dev_private;
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+ u32 dvo_reg = dvo->dvo_reg;
+ u32 temp = I915_READ(dvo_reg);
+
+ if (mode == DRM_MODE_DPMS_ON) {
+ I915_WRITE(dvo_reg, temp | DVO_ENABLE);
+ I915_READ(dvo_reg);
+ dvo->dev_ops->dpms(dvo, mode);
+ } else {
+ dvo->dev_ops->dpms(dvo, mode);
+ I915_WRITE(dvo_reg, temp & ~DVO_ENABLE);
+ I915_READ(dvo_reg);
+ }
+}
+
+static void intel_dvo_save(struct drm_connector *connector)
+{
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ /* Each output should probably just save the registers it touches,
+ * but for now, use more overkill.
+ */
+ dev_priv->saveDVOA = I915_READ(DVOA);
+ dev_priv->saveDVOB = I915_READ(DVOB);
+ dev_priv->saveDVOC = I915_READ(DVOC);
+
+ dvo->dev_ops->save(dvo);
+}
+
+static void intel_dvo_restore(struct drm_connector *connector)
+{
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ dvo->dev_ops->restore(dvo);
+
+ I915_WRITE(DVOA, dev_priv->saveDVOA);
+ I915_WRITE(DVOB, dev_priv->saveDVOB);
+ I915_WRITE(DVOC, dev_priv->saveDVOC);
+}
+
+static int intel_dvo_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ /* XXX: Validate clock range */
+
+ if (dvo->panel_fixed_mode) {
+ if (mode->hdisplay > dvo->panel_fixed_mode->hdisplay)
+ return MODE_PANEL;
+ if (mode->vdisplay > dvo->panel_fixed_mode->vdisplay)
+ return MODE_PANEL;
+ }
+
+ return dvo->dev_ops->mode_valid(dvo, mode);
+}
+
+static bool intel_dvo_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ /* If we have timings from the BIOS for the panel, put them in
+ * to the adjusted mode. The CRTC will be set up for this mode,
+ * with the panel scaling set up to source from the H/VDisplay
+ * of the original mode.
+ */
+ if (dvo->panel_fixed_mode != NULL) {
+#define C(x) adjusted_mode->x = dvo->panel_fixed_mode->x
+ C(hdisplay);
+ C(hsync_start);
+ C(hsync_end);
+ C(htotal);
+ C(vdisplay);
+ C(vsync_start);
+ C(vsync_end);
+ C(vtotal);
+ C(clock);
+ drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
+#undef C
+ }
+
+ if (dvo->dev_ops->mode_fixup)
+ return dvo->dev_ops->mode_fixup(dvo, mode, adjusted_mode);
+
+ return true;
+}
+
+static void intel_dvo_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+ int pipe = intel_crtc->pipe;
+ u32 dvo_val;
+ u32 dvo_reg = dvo->dvo_reg, dvo_srcdim_reg;
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+
+ switch (dvo_reg) {
+ case DVOA:
+ default:
+ dvo_srcdim_reg = DVOA_SRCDIM;
+ break;
+ case DVOB:
+ dvo_srcdim_reg = DVOB_SRCDIM;
+ break;
+ case DVOC:
+ dvo_srcdim_reg = DVOC_SRCDIM;
+ break;
+ }
+
+ dvo->dev_ops->mode_set(dvo, mode, adjusted_mode);
+
+ /* Save the data order, since I don't know what it should be set to. */
+ dvo_val = I915_READ(dvo_reg) &
+ (DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG);
+ dvo_val |= DVO_DATA_ORDER_FP | DVO_BORDER_ENABLE |
+ DVO_BLANK_ACTIVE_HIGH;
+
+ if (pipe == 1)
+ dvo_val |= DVO_PIPE_B_SELECT;
+ dvo_val |= DVO_PIPE_STALL;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ dvo_val |= DVO_HSYNC_ACTIVE_HIGH;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ dvo_val |= DVO_VSYNC_ACTIVE_HIGH;
+
+ I915_WRITE(dpll_reg, I915_READ(dpll_reg) | DPLL_DVO_HIGH_SPEED);
+
+ /*I915_WRITE(DVOB_SRCDIM,
+ (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) |
+ (adjusted_mode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/
+ I915_WRITE(dvo_srcdim_reg,
+ (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) |
+ (adjusted_mode->vdisplay << DVO_SRCDIM_VERTICAL_SHIFT));
+ /*I915_WRITE(DVOB, dvo_val);*/
+ I915_WRITE(dvo_reg, dvo_val);
+}
+
+/**
+ * Detect the output connection on our DVO device.
+ *
+ * Unimplemented.
+ */
+static enum drm_connector_status intel_dvo_detect(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ return dvo->dev_ops->detect(dvo);
+}
+
+static int intel_dvo_get_modes(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ /* We should probably have an i2c driver get_modes function for those
+ * devices which will have a fixed set of modes determined by the chip
+ * (TV-out, for example), but for now with just TMDS and LVDS,
+ * that's not the case.
+ */
+ intel_ddc_get_modes(intel_output);
+ if (!list_empty(&connector->probed_modes))
+ return 1;
+
+
+ if (dvo->panel_fixed_mode != NULL) {
+ struct drm_display_mode *mode;
+ mode = drm_mode_duplicate(connector->dev, dvo->panel_fixed_mode);
+ if (mode) {
+ drm_mode_probed_add(connector, mode);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void intel_dvo_destroy (struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ if (dvo) {
+ if (dvo->dev_ops->destroy)
+ dvo->dev_ops->destroy(dvo);
+ if (dvo->panel_fixed_mode)
+ kfree(dvo->panel_fixed_mode);
+ /* no need, in i830_dvoices[] now */
+ //kfree(dvo);
+ }
+ if (intel_output->i2c_bus)
+ intel_i2c_destroy(intel_output->i2c_bus);
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(intel_output);
+}
+
+#ifdef RANDR_GET_CRTC_INTERFACE
+static struct drm_crtc *intel_dvo_get_crtc(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+ int pipe = !!(I915_READ(dvo->dvo_reg) & SDVO_PIPE_B_SELECT);
+
+ return intel_pipe_to_crtc(pScrn, pipe);
+}
+#endif
+
+static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = {
+ .dpms = intel_dvo_dpms,
+ .mode_fixup = intel_dvo_mode_fixup,
+ .prepare = intel_encoder_prepare,
+ .mode_set = intel_dvo_mode_set,
+ .commit = intel_encoder_commit,
+};
+
+static const struct drm_connector_funcs intel_dvo_connector_funcs = {
+ .save = intel_dvo_save,
+ .restore = intel_dvo_restore,
+ .detect = intel_dvo_detect,
+ .destroy = intel_dvo_destroy,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+};
+
+static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = {
+ .mode_valid = intel_dvo_mode_valid,
+ .get_modes = intel_dvo_get_modes,
+ .best_encoder = intel_best_encoder,
+};
+
+static void intel_dvo_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_dvo_enc_funcs = {
+ .destroy = intel_dvo_enc_destroy,
+};
+
+
+/**
+ * Attempts to get a fixed panel timing for LVDS (currently only the i830).
+ *
+ * Other chips with DVO LVDS will need to extend this to deal with the LVDS
+ * chip being on DVOB/C and having multiple pipes.
+ */
+static struct drm_display_mode *
+intel_dvo_get_current_mode (struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+ uint32_t dvo_reg = dvo->dvo_reg;
+ uint32_t dvo_val = I915_READ(dvo_reg);
+ struct drm_display_mode *mode = NULL;
+
+ /* If the DVO port is active, that'll be the LVDS, so we can pull out
+ * its timings to get how the BIOS set up the panel.
+ */
+ if (dvo_val & DVO_ENABLE) {
+ struct drm_crtc *crtc;
+ int pipe = (dvo_val & DVO_PIPE_B_SELECT) ? 1 : 0;
+
+ crtc = intel_get_crtc_from_pipe(dev, pipe);
+ if (crtc) {
+ mode = intel_crtc_mode_get(dev, crtc);
+
+ if (mode) {
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+ if (dvo_val & DVO_HSYNC_ACTIVE_HIGH)
+ mode->flags |= DRM_MODE_FLAG_PHSYNC;
+ if (dvo_val & DVO_VSYNC_ACTIVE_HIGH)
+ mode->flags |= DRM_MODE_FLAG_PVSYNC;
+ }
+ }
+ }
+ return mode;
+}
+
+void intel_dvo_init(struct drm_device *dev)
+{
+ struct intel_output *intel_output;
+ struct intel_dvo_device *dvo;
+ struct intel_i2c_chan *i2cbus = NULL;
+ int ret = 0;
+ int i;
+ int gpio_inited = 0;
+ int encoder_type = DRM_MODE_ENCODER_NONE;
+ intel_output = kzalloc (sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output)
+ return;
+
+ /* Set up the DDC bus */
+ intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "DVODDC_D");
+ if (!intel_output->ddc_bus)
+ goto free_intel;
+
+ /* Now, try to find a controller */
+ for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) {
+ struct drm_connector *connector = &intel_output->base;
+ int gpio;
+
+ dvo = &intel_dvo_devices[i];
+
+ /* Allow the I2C driver info to specify the GPIO to be used in
+ * special cases, but otherwise default to what's defined
+ * in the spec.
+ */
+ if (dvo->gpio != 0)
+ gpio = dvo->gpio;
+ else if (dvo->type == INTEL_DVO_CHIP_LVDS)
+ gpio = GPIOB;
+ else
+ gpio = GPIOE;
+
+ /* Set up the I2C bus necessary for the chip we're probing.
+ * It appears that everything is on GPIOE except for panels
+ * on i830 laptops, which are on GPIOB (DVOA).
+ */
+ if (gpio_inited != gpio) {
+ if (i2cbus != NULL)
+ intel_i2c_destroy(i2cbus);
+ if (!(i2cbus = intel_i2c_create(dev, gpio,
+ gpio == GPIOB ? "DVOI2C_B" : "DVOI2C_E"))) {
+ continue;
+ }
+ gpio_inited = gpio;
+ }
+
+ if (dvo->dev_ops!= NULL)
+ ret = dvo->dev_ops->init(dvo, i2cbus);
+ else
+ ret = false;
+
+ if (!ret)
+ continue;
+
+ intel_output->type = INTEL_OUTPUT_DVO;
+ switch (dvo->type) {
+ case INTEL_DVO_CHIP_TMDS:
+ drm_connector_init(dev, connector,
+ &intel_dvo_connector_funcs,
+ DRM_MODE_CONNECTOR_DVII);
+ encoder_type = DRM_MODE_ENCODER_TMDS;
+ break;
+ case INTEL_DVO_CHIP_LVDS:
+ drm_connector_init(dev, connector,
+ &intel_dvo_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+ encoder_type = DRM_MODE_ENCODER_LVDS;
+ break;
+ }
+
+ drm_connector_helper_add(connector,
+ &intel_dvo_connector_helper_funcs);
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+ intel_output->dev_priv = dvo;
+ intel_output->i2c_bus = i2cbus;
+
+ drm_encoder_init(dev, &intel_output->enc,
+ &intel_dvo_enc_funcs, encoder_type);
+ drm_encoder_helper_add(&intel_output->enc,
+ &intel_dvo_helper_funcs);
+
+ drm_mode_connector_attach_encoder(&intel_output->base,
+ &intel_output->enc);
+ if (dvo->type == INTEL_DVO_CHIP_LVDS) {
+ /* For our LVDS chipsets, we should hopefully be able
+ * to dig the fixed panel mode out of the BIOS data.
+ * However, it's in a different format from the BIOS
+ * data on chipsets with integrated LVDS (stored in AIM
+ * headers, likely), so for now, just get the current
+ * mode being output through DVO.
+ */
+ dvo->panel_fixed_mode =
+ intel_dvo_get_current_mode(connector);
+ dvo->panel_wants_dither = true;
+ }
+
+ drm_sysfs_connector_add(connector);
+ return;
+ }
+
+ intel_i2c_destroy(intel_output->ddc_bus);
+ /* Didn't find a chip, so tear down. */
+ if (i2cbus != NULL)
+ intel_i2c_destroy(i2cbus);
+free_intel:
+ kfree(intel_output);
+}
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
new file mode 100644
index 000000000000..afd1217b8a02
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -0,0 +1,925 @@
+/*
+ * Copyright © 2007 David Airlie
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * David Airlie
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/sysrq.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+struct intelfb_par {
+ struct drm_device *dev;
+ struct drm_display_mode *our_mode;
+ struct intel_framebuffer *intel_fb;
+ int crtc_count;
+ /* crtc currently bound to this */
+ uint32_t crtc_ids[2];
+};
+
+static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_crtc *crtc;
+ int i;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_mode_set *modeset = &intel_crtc->mode_set;
+ struct drm_framebuffer *fb = modeset->fb;
+
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ if (i == par->crtc_count)
+ continue;
+
+
+ if (regno > 255)
+ return 1;
+
+ if (fb->depth == 8) {
+ intel_crtc_fb_gamma_set(crtc, red, green, blue, regno);
+ return 0;
+ }
+
+ if (regno < 16) {
+ switch (fb->depth) {
+ case 15:
+ fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
+ ((green & 0xf800) >> 6) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 16:
+ fb->pseudo_palette[regno] = (red & 0xf800) |
+ ((green & 0xfc00) >> 5) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 24:
+ case 32:
+ fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
+ (green & 0xff00) |
+ ((blue & 0xff00) >> 8);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int intelfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct intel_framebuffer *intel_fb = par->intel_fb;
+ struct drm_framebuffer *fb = &intel_fb->base;
+ int depth;
+
+ if (var->pixclock == -1 || !var->pixclock)
+ return -EINVAL;
+
+ /* Need to resize the fb object !!! */
+ if (var->xres > fb->width || var->yres > fb->height) {
+ DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height);
+ DRM_ERROR("Need resizing code.\n");
+ return -EINVAL;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ depth = (var->green.length == 6) ? 16 : 15;
+ break;
+ case 32:
+ depth = (var->transp.length > 0) ? 32 : 24;
+ break;
+ default:
+ depth = var->bits_per_pixel;
+ break;
+ }
+
+ switch (depth) {
+ case 8:
+ var->red.offset = 0;
+ var->green.offset = 0;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 15:
+ var->red.offset = 10;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ var->transp.length = 1;
+ var->transp.offset = 15;
+ break;
+ case 16:
+ var->red.offset = 11;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 6;
+ var->blue.length = 5;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 24:
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 32:
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* this will let fbcon do the mode init */
+/* FIXME: take mode config lock? */
+static int intelfb_set_par(struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct fb_var_screeninfo *var = &info->var;
+ int i;
+
+ DRM_DEBUG("%d %d\n", var->xres, var->pixclock);
+
+ if (var->pixclock != -1) {
+
+ DRM_ERROR("PIXEL CLCOK SET\n");
+ return -EINVAL;
+ } else {
+ struct drm_crtc *crtc;
+ int ret;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ if (i == par->crtc_count)
+ continue;
+
+ if (crtc->fb == intel_crtc->mode_set.fb) {
+ mutex_lock(&dev->mode_config.mutex);
+ ret = crtc->funcs->set_config(&intel_crtc->mode_set);
+ mutex_unlock(&dev->mode_config.mutex);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+ }
+}
+
+static int intelfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_mode_set *modeset;
+ struct drm_crtc *crtc;
+ struct intel_crtc *intel_crtc;
+ int ret = 0;
+ int i;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ if (i == par->crtc_count)
+ continue;
+
+ intel_crtc = to_intel_crtc(crtc);
+ modeset = &intel_crtc->mode_set;
+
+ modeset->x = var->xoffset;
+ modeset->y = var->yoffset;
+
+ if (modeset->num_connectors) {
+ mutex_lock(&dev->mode_config.mutex);
+ ret = crtc->funcs->set_config(modeset);
+ mutex_unlock(&dev->mode_config.mutex);
+ if (!ret) {
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void intelfb_on(struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
+ int i;
+
+ /*
+ * For each CRTC in this fb, find all associated encoders
+ * and turn them off, then turn off the CRTC.
+ */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+
+ /* Found a CRTC on this fb, now find encoders */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->crtc == crtc) {
+ struct drm_encoder_helper_funcs *encoder_funcs;
+ encoder_funcs = encoder->helper_private;
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+ }
+ }
+ }
+}
+
+static void intelfb_off(struct fb_info *info, int dpms_mode)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
+ int i;
+
+ /*
+ * For each CRTC in this fb, find all associated encoders
+ * and turn them off, then turn off the CRTC.
+ */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ /* Found a CRTC on this fb, now find encoders */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->crtc == crtc) {
+ struct drm_encoder_helper_funcs *encoder_funcs;
+ encoder_funcs = encoder->helper_private;
+ encoder_funcs->dpms(encoder, dpms_mode);
+ }
+ }
+ if (dpms_mode == DRM_MODE_DPMS_OFF)
+ crtc_funcs->dpms(crtc, dpms_mode);
+ }
+}
+
+static int intelfb_blank(int blank, struct fb_info *info)
+{
+ switch (blank) {
+ case FB_BLANK_UNBLANK:
+ intelfb_on(info);
+ break;
+ case FB_BLANK_NORMAL:
+ intelfb_off(info, DRM_MODE_DPMS_STANDBY);
+ break;
+ case FB_BLANK_HSYNC_SUSPEND:
+ intelfb_off(info, DRM_MODE_DPMS_STANDBY);
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ intelfb_off(info, DRM_MODE_DPMS_SUSPEND);
+ break;
+ case FB_BLANK_POWERDOWN:
+ intelfb_off(info, DRM_MODE_DPMS_OFF);
+ break;
+ }
+ return 0;
+}
+
+static struct fb_ops intelfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = intelfb_check_var,
+ .fb_set_par = intelfb_set_par,
+ .fb_setcolreg = intelfb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_pan_display = intelfb_pan_display,
+ .fb_blank = intelfb_blank,
+};
+
+/**
+ * Curretly it is assumed that the old framebuffer is reused.
+ *
+ * LOCKING
+ * caller should hold the mode config lock.
+ *
+ */
+int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc)
+{
+ struct fb_info *info;
+ struct drm_framebuffer *fb;
+ struct drm_display_mode *mode = crtc->desired_mode;
+
+ fb = crtc->fb;
+ if (!fb)
+ return 1;
+
+ info = fb->fbdev;
+ if (!info)
+ return 1;
+
+ if (!mode)
+ return 1;
+
+ info->var.xres = mode->hdisplay;
+ info->var.right_margin = mode->hsync_start - mode->hdisplay;
+ info->var.hsync_len = mode->hsync_end - mode->hsync_start;
+ info->var.left_margin = mode->htotal - mode->hsync_end;
+ info->var.yres = mode->vdisplay;
+ info->var.lower_margin = mode->vsync_start - mode->vdisplay;
+ info->var.vsync_len = mode->vsync_end - mode->vsync_start;
+ info->var.upper_margin = mode->vtotal - mode->vsync_end;
+ info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100;
+ /* avoid overflow */
+ info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
+
+ return 0;
+}
+EXPORT_SYMBOL(intelfb_resize);
+
+static struct drm_mode_set kernelfb_mode;
+
+static int intelfb_panic(struct notifier_block *n, unsigned long ununsed,
+ void *panic_str)
+{
+ DRM_ERROR("panic occurred, switching back to text console\n");
+
+ intelfb_restore();
+ return 0;
+}
+
+static struct notifier_block paniced = {
+ .notifier_call = intelfb_panic,
+};
+
+static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
+ uint32_t fb_height, uint32_t surface_width,
+ uint32_t surface_height,
+ struct intel_framebuffer **intel_fb_p)
+{
+ struct fb_info *info;
+ struct intelfb_par *par;
+ struct drm_framebuffer *fb;
+ struct intel_framebuffer *intel_fb;
+ struct drm_mode_fb_cmd mode_cmd;
+ struct drm_gem_object *fbo = NULL;
+ struct drm_i915_gem_object *obj_priv;
+ struct device *device = &dev->pdev->dev;
+ int size, ret, mmio_bar = IS_I9XX(dev) ? 0 : 1;
+
+ mode_cmd.width = surface_width;
+ mode_cmd.height = surface_height;
+
+ mode_cmd.bpp = 32;
+ mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 1) / 8), 64);
+ mode_cmd.depth = 24;
+
+ size = mode_cmd.pitch * mode_cmd.height;
+ size = ALIGN(size, PAGE_SIZE);
+ fbo = drm_gem_object_alloc(dev, size);
+ if (!fbo) {
+ printk(KERN_ERR "failed to allocate framebuffer\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ obj_priv = fbo->driver_private;
+
+ mutex_lock(&dev->struct_mutex);
+
+ ret = i915_gem_object_pin(fbo, PAGE_SIZE);
+ if (ret) {
+ DRM_ERROR("failed to pin fb: %d\n", ret);
+ goto out_unref;
+ }
+
+ /* Flush everything out, we'll be doing GTT only from now on */
+ i915_gem_object_set_to_gtt_domain(fbo, 1);
+
+ ret = intel_framebuffer_create(dev, &mode_cmd, &fb, fbo);
+ if (ret) {
+ DRM_ERROR("failed to allocate fb.\n");
+ goto out_unref;
+ }
+
+ list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
+
+ intel_fb = to_intel_framebuffer(fb);
+ *intel_fb_p = intel_fb;
+
+ info = framebuffer_alloc(sizeof(struct intelfb_par), device);
+ if (!info) {
+ ret = -ENOMEM;
+ goto out_unref;
+ }
+
+ par = info->par;
+
+ strcpy(info->fix.id, "inteldrmfb");
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.type_aux = 0;
+ info->fix.xpanstep = 1; /* doing it in hw */
+ info->fix.ypanstep = 1; /* doing it in hw */
+ info->fix.ywrapstep = 0;
+ info->fix.accel = FB_ACCEL_I830;
+ info->fix.type_aux = 0;
+
+ info->flags = FBINFO_DEFAULT;
+
+ info->fbops = &intelfb_ops;
+
+ info->fix.line_length = fb->pitch;
+ info->fix.smem_start = dev->mode_config.fb_base + obj_priv->gtt_offset;
+ info->fix.smem_len = size;
+
+ info->flags = FBINFO_DEFAULT;
+
+ info->screen_base = ioremap_wc(dev->agp->base + obj_priv->gtt_offset,
+ size);
+ if (!info->screen_base) {
+ ret = -ENOSPC;
+ goto out_unref;
+ }
+ info->screen_size = size;
+
+// memset(info->screen_base, 0, size);
+
+ info->pseudo_palette = fb->pseudo_palette;
+ info->var.xres_virtual = fb->width;
+ info->var.yres_virtual = fb->height;
+ info->var.bits_per_pixel = fb->bits_per_pixel;
+ info->var.xoffset = 0;
+ info->var.yoffset = 0;
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->var.height = -1;
+ info->var.width = -1;
+
+ info->var.xres = fb_width;
+ info->var.yres = fb_height;
+
+ /* FIXME: we really shouldn't expose mmio space at all */
+ info->fix.mmio_start = pci_resource_start(dev->pdev, mmio_bar);
+ info->fix.mmio_len = pci_resource_len(dev->pdev, mmio_bar);
+
+ info->pixmap.size = 64*1024;
+ info->pixmap.buf_align = 8;
+ info->pixmap.access_align = 32;
+ info->pixmap.flags = FB_PIXMAP_SYSTEM;
+ info->pixmap.scan_align = 1;
+
+ switch(fb->depth) {
+ case 8:
+ info->var.red.offset = 0;
+ info->var.green.offset = 0;
+ info->var.blue.offset = 0;
+ info->var.red.length = 8; /* 8bit DAC */
+ info->var.green.length = 8;
+ info->var.blue.length = 8;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+ break;
+ case 15:
+ info->var.red.offset = 10;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = 5;
+ info->var.green.length = 5;
+ info->var.blue.length = 5;
+ info->var.transp.offset = 15;
+ info->var.transp.length = 1;
+ break;
+ case 16:
+ info->var.red.offset = 11;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = 5;
+ info->var.green.length = 6;
+ info->var.blue.length = 5;
+ info->var.transp.offset = 0;
+ break;
+ case 24:
+ info->var.red.offset = 16;
+ info->var.green.offset = 8;
+ info->var.blue.offset = 0;
+ info->var.red.length = 8;
+ info->var.green.length = 8;
+ info->var.blue.length = 8;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+ break;
+ case 32:
+ info->var.red.offset = 16;
+ info->var.green.offset = 8;
+ info->var.blue.offset = 0;
+ info->var.red.length = 8;
+ info->var.green.length = 8;
+ info->var.blue.length = 8;
+ info->var.transp.offset = 24;
+ info->var.transp.length = 8;
+ break;
+ default:
+ break;
+ }
+
+ fb->fbdev = info;
+
+ par->intel_fb = intel_fb;
+ par->dev = dev;
+
+ /* To allow resizeing without swapping buffers */
+ printk("allocated %dx%d fb: 0x%08x, bo %p\n", intel_fb->base.width,
+ intel_fb->base.height, obj_priv->gtt_offset, fbo);
+
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+
+out_unref:
+ drm_gem_object_unreference(fbo);
+ mutex_unlock(&dev->struct_mutex);
+out:
+ return ret;
+}
+
+static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *crtc)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_framebuffer *intel_fb;
+ struct drm_framebuffer *fb;
+ struct drm_connector *connector;
+ struct fb_info *info;
+ struct intelfb_par *par;
+ struct drm_mode_set *modeset;
+ unsigned int width, height;
+ int new_fb = 0;
+ int ret, i, conn_count;
+
+ if (!drm_helper_crtc_in_use(crtc))
+ return 0;
+
+ if (!crtc->desired_mode)
+ return 0;
+
+ width = crtc->desired_mode->hdisplay;
+ height = crtc->desired_mode->vdisplay;
+
+ /* is there an fb bound to this crtc already */
+ if (!intel_crtc->mode_set.fb) {
+ ret = intelfb_create(dev, width, height, width, height, &intel_fb);
+ if (ret)
+ return -EINVAL;
+ new_fb = 1;
+ } else {
+ fb = intel_crtc->mode_set.fb;
+ intel_fb = to_intel_framebuffer(fb);
+ if ((intel_fb->base.width < width) || (intel_fb->base.height < height))
+ return -EINVAL;
+ }
+
+ info = intel_fb->base.fbdev;
+ par = info->par;
+
+ modeset = &intel_crtc->mode_set;
+ modeset->fb = &intel_fb->base;
+ conn_count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder)
+ if (connector->encoder->crtc == modeset->crtc) {
+ modeset->connectors[conn_count] = connector;
+ conn_count++;
+ if (conn_count > INTELFB_CONN_LIMIT)
+ BUG();
+ }
+ }
+
+ for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
+ modeset->connectors[i] = NULL;
+
+ par->crtc_ids[0] = crtc->base.id;
+
+ modeset->num_connectors = conn_count;
+ if (modeset->mode != modeset->crtc->desired_mode)
+ modeset->mode = modeset->crtc->desired_mode;
+
+ par->crtc_count = 1;
+
+ if (new_fb) {
+ info->var.pixclock = -1;
+ if (register_framebuffer(info) < 0)
+ return -EINVAL;
+ } else
+ intelfb_set_par(info);
+
+ printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
+ info->fix.id);
+
+ /* Switch back to kernel console on panic */
+ kernelfb_mode = *modeset;
+ atomic_notifier_chain_register(&panic_notifier_list, &paniced);
+ printk(KERN_INFO "registered panic notifier\n");
+
+ return 0;
+}
+
+static int intelfb_multi_fb_probe(struct drm_device *dev)
+{
+
+ struct drm_crtc *crtc;
+ int ret = 0;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ ret = intelfb_multi_fb_probe_crtc(dev, crtc);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static int intelfb_single_fb_probe(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+ struct drm_connector *connector;
+ unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
+ unsigned int surface_width = 0, surface_height = 0;
+ int new_fb = 0;
+ int crtc_count = 0;
+ int ret, i, conn_count = 0;
+ struct intel_framebuffer *intel_fb;
+ struct fb_info *info;
+ struct intelfb_par *par;
+ struct drm_mode_set *modeset = NULL;
+
+ DRM_DEBUG("\n");
+
+ /* Get a count of crtcs now in use and new min/maxes width/heights */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (!drm_helper_crtc_in_use(crtc))
+ continue;
+
+ crtc_count++;
+ if (!crtc->desired_mode)
+ continue;
+
+ /* Smallest mode determines console size... */
+ if (crtc->desired_mode->hdisplay < fb_width)
+ fb_width = crtc->desired_mode->hdisplay;
+
+ if (crtc->desired_mode->vdisplay < fb_height)
+ fb_height = crtc->desired_mode->vdisplay;
+
+ /* ... but largest for memory allocation dimensions */
+ if (crtc->desired_mode->hdisplay > surface_width)
+ surface_width = crtc->desired_mode->hdisplay;
+
+ if (crtc->desired_mode->vdisplay > surface_height)
+ surface_height = crtc->desired_mode->vdisplay;
+ }
+
+ if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
+ /* hmm everyone went away - assume VGA cable just fell out
+ and will come back later. */
+ DRM_DEBUG("no CRTCs available?\n");
+ return 0;
+ }
+
+//fail
+ /* Find the fb for our new config */
+ if (list_empty(&dev->mode_config.fb_kernel_list)) {
+ DRM_DEBUG("creating new fb (console size %dx%d, "
+ "buffer size %dx%d)\n", fb_width, fb_height,
+ surface_width, surface_height);
+ ret = intelfb_create(dev, fb_width, fb_height, surface_width,
+ surface_height, &intel_fb);
+ if (ret)
+ return -EINVAL;
+ new_fb = 1;
+ } else {
+ struct drm_framebuffer *fb;
+
+ fb = list_first_entry(&dev->mode_config.fb_kernel_list,
+ struct drm_framebuffer, filp_head);
+ intel_fb = to_intel_framebuffer(fb);
+
+ /* if someone hotplugs something bigger than we have already
+ * allocated, we are pwned. As really we can't resize an
+ * fbdev that is in the wild currently due to fbdev not really
+ * being designed for the lower layers moving stuff around
+ * under it.
+ * - so in the grand style of things - punt.
+ */
+ if ((fb->width < surface_width) ||
+ (fb->height < surface_height)) {
+ DRM_ERROR("fb not large enough for console\n");
+ return -EINVAL;
+ }
+ }
+// fail
+
+ info = intel_fb->base.fbdev;
+ par = info->par;
+
+ crtc_count = 0;
+ /*
+ * For each CRTC, set up the connector list for the CRTC's mode
+ * set configuration.
+ */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ modeset = &intel_crtc->mode_set;
+ modeset->fb = &intel_fb->base;
+ conn_count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list,
+ head) {
+ if (!connector->encoder)
+ continue;
+
+ if(connector->encoder->crtc == modeset->crtc) {
+ modeset->connectors[conn_count++] = connector;
+ if (conn_count > INTELFB_CONN_LIMIT)
+ BUG();
+ }
+ }
+
+ /* Zero out remaining connector pointers */
+ for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
+ modeset->connectors[i] = NULL;
+
+ par->crtc_ids[crtc_count++] = crtc->base.id;
+
+ modeset->num_connectors = conn_count;
+ if (modeset->mode != modeset->crtc->desired_mode)
+ modeset->mode = modeset->crtc->desired_mode;
+ }
+ par->crtc_count = crtc_count;
+
+ if (new_fb) {
+ info->var.pixclock = -1;
+ if (register_framebuffer(info) < 0)
+ return -EINVAL;
+ } else
+ intelfb_set_par(info);
+
+ printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
+ info->fix.id);
+
+ /* Switch back to kernel console on panic */
+ kernelfb_mode = *modeset;
+ atomic_notifier_chain_register(&panic_notifier_list, &paniced);
+ printk(KERN_INFO "registered panic notifier\n");
+
+ return 0;
+}
+
+/**
+ * intelfb_restore - restore the framebuffer console (kernel) config
+ *
+ * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
+ */
+void intelfb_restore(void)
+{
+ drm_crtc_helper_set_config(&kernelfb_mode);
+}
+
+static void intelfb_sysrq(int dummy1, struct tty_struct *dummy3)
+{
+ intelfb_restore();
+}
+
+static struct sysrq_key_op sysrq_intelfb_restore_op = {
+ .handler = intelfb_sysrq,
+ .help_msg = "force fb",
+ .action_msg = "force restore of fb console",
+};
+
+int intelfb_probe(struct drm_device *dev)
+{
+ int ret;
+
+ DRM_DEBUG("\n");
+
+ /* something has changed in the lower levels of hell - deal with it
+ here */
+
+ /* two modes : a) 1 fb to rule all crtcs.
+ b) one fb per crtc.
+ two actions 1) new connected device
+ 2) device removed.
+ case a/1 : if the fb surface isn't big enough - resize the surface fb.
+ if the fb size isn't big enough - resize fb into surface.
+ if everything big enough configure the new crtc/etc.
+ case a/2 : undo the configuration
+ possibly resize down the fb to fit the new configuration.
+ case b/1 : see if it is on a new crtc - setup a new fb and add it.
+ case b/2 : teardown the new fb.
+ */
+
+ /* mode a first */
+ /* search for an fb */
+ if (i915_fbpercrtc == 1) {
+ ret = intelfb_multi_fb_probe(dev);
+ } else {
+ ret = intelfb_single_fb_probe(dev);
+ }
+
+ register_sysrq_key('g', &sysrq_intelfb_restore_op);
+
+ return ret;
+}
+EXPORT_SYMBOL(intelfb_probe);
+
+int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
+{
+ struct fb_info *info;
+
+ if (!fb)
+ return -EINVAL;
+
+ info = fb->fbdev;
+
+ if (info) {
+ unregister_framebuffer(info);
+ iounmap(info->screen_base);
+ framebuffer_release(info);
+ }
+
+ atomic_notifier_chain_unregister(&panic_notifier_list, &paniced);
+ memset(&kernelfb_mode, 0, sizeof(struct drm_mode_set));
+ return 0;
+}
+EXPORT_SYMBOL(intelfb_remove);
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
new file mode 100644
index 000000000000..a5a2f5339e9e
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright © 2006-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include "drmP.h"
+#include "drm.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+/*
+ * Intel GPIO access functions
+ */
+
+#define I2C_RISEFALL_TIME 20
+
+static int get_clock(void *data)
+{
+ struct intel_i2c_chan *chan = data;
+ struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
+ u32 val;
+
+ val = I915_READ(chan->reg);
+ return ((val & GPIO_CLOCK_VAL_IN) != 0);
+}
+
+static int get_data(void *data)
+{
+ struct intel_i2c_chan *chan = data;
+ struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
+ u32 val;
+
+ val = I915_READ(chan->reg);
+ return ((val & GPIO_DATA_VAL_IN) != 0);
+}
+
+static void set_clock(void *data, int state_high)
+{
+ struct intel_i2c_chan *chan = data;
+ struct drm_device *dev = chan->drm_dev;
+ struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
+ u32 reserved = 0, clock_bits;
+
+ /* On most chips, these bits must be preserved in software. */
+ if (!IS_I830(dev) && !IS_845G(dev))
+ reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE |
+ GPIO_CLOCK_PULLUP_DISABLE);
+
+ if (state_high)
+ clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK;
+ else
+ clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
+ GPIO_CLOCK_VAL_MASK;
+ I915_WRITE(chan->reg, reserved | clock_bits);
+ udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
+}
+
+static void set_data(void *data, int state_high)
+{
+ struct intel_i2c_chan *chan = data;
+ struct drm_device *dev = chan->drm_dev;
+ struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
+ u32 reserved = 0, data_bits;
+
+ /* On most chips, these bits must be preserved in software. */
+ if (!IS_I830(dev) && !IS_845G(dev))
+ reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE |
+ GPIO_CLOCK_PULLUP_DISABLE);
+
+ if (state_high)
+ data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK;
+ else
+ data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
+ GPIO_DATA_VAL_MASK;
+
+ I915_WRITE(chan->reg, reserved | data_bits);
+ udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
+}
+
+/**
+ * intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
+ * @dev: DRM device
+ * @output: driver specific output device
+ * @reg: GPIO reg to use
+ * @name: name for this bus
+ *
+ * Creates and registers a new i2c bus with the Linux i2c layer, for use
+ * in output probing and control (e.g. DDC or SDVO control functions).
+ *
+ * Possible values for @reg include:
+ * %GPIOA
+ * %GPIOB
+ * %GPIOC
+ * %GPIOD
+ * %GPIOE
+ * %GPIOF
+ * %GPIOG
+ * %GPIOH
+ * see PRM for details on how these different busses are used.
+ */
+struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg,
+ const char *name)
+{
+ struct intel_i2c_chan *chan;
+
+ chan = kzalloc(sizeof(struct intel_i2c_chan), GFP_KERNEL);
+ if (!chan)
+ goto out_free;
+
+ chan->drm_dev = dev;
+ chan->reg = reg;
+ snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name);
+ chan->adapter.owner = THIS_MODULE;
+#ifndef I2C_HW_B_INTELFB
+#define I2C_HW_B_INTELFB I2C_HW_B_I810
+#endif
+ chan->adapter.id = I2C_HW_B_INTELFB;
+ chan->adapter.algo_data = &chan->algo;
+ chan->adapter.dev.parent = &dev->pdev->dev;
+ chan->algo.setsda = set_data;
+ chan->algo.setscl = set_clock;
+ chan->algo.getsda = get_data;
+ chan->algo.getscl = get_clock;
+ chan->algo.udelay = 20;
+ chan->algo.timeout = usecs_to_jiffies(2200);
+ chan->algo.data = chan;
+
+ i2c_set_adapdata(&chan->adapter, chan);
+
+ if(i2c_bit_add_bus(&chan->adapter))
+ goto out_free;
+
+ /* JJJ: raise SCL and SDA? */
+ set_data(chan, 1);
+ set_clock(chan, 1);
+ udelay(20);
+
+ return chan;
+
+out_free:
+ kfree(chan);
+ return NULL;
+}
+
+/**
+ * intel_i2c_destroy - unregister and free i2c bus resources
+ * @output: channel to free
+ *
+ * Unregister the adapter from the i2c layer, then free the structure.
+ */
+void intel_i2c_destroy(struct intel_i2c_chan *chan)
+{
+ if (!chan)
+ return;
+
+ i2c_del_adapter(&chan->adapter);
+ kfree(chan);
+}
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
new file mode 100644
index 000000000000..ccecfaf6307b
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ * Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_edid.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+/**
+ * Sets the backlight level.
+ *
+ * \param level backlight level, from 0 to intel_lvds_get_max_backlight().
+ */
+static void intel_lvds_set_backlight(struct drm_device *dev, int level)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 blc_pwm_ctl;
+
+ blc_pwm_ctl = I915_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+ I915_WRITE(BLC_PWM_CTL, (blc_pwm_ctl |
+ (level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
+}
+
+/**
+ * Returns the maximum level of the backlight duty cycle field.
+ */
+static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ return ((I915_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >>
+ BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+}
+
+/**
+ * Sets the power state for the panel.
+ */
+static void intel_lvds_set_power(struct drm_device *dev, bool on)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pp_status;
+
+ if (on) {
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) |
+ POWER_TARGET_ON);
+ do {
+ pp_status = I915_READ(PP_STATUS);
+ } while ((pp_status & PP_ON) == 0);
+
+ intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle);
+ } else {
+ intel_lvds_set_backlight(dev, 0);
+
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) &
+ ~POWER_TARGET_ON);
+ do {
+ pp_status = I915_READ(PP_STATUS);
+ } while (pp_status & PP_ON);
+ }
+}
+
+static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+
+ if (mode == DRM_MODE_DPMS_ON)
+ intel_lvds_set_power(dev, true);
+ else
+ intel_lvds_set_power(dev, false);
+
+ /* XXX: We never power down the LVDS pairs. */
+}
+
+static void intel_lvds_save(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->savePP_ON = I915_READ(PP_ON_DELAYS);
+ dev_priv->savePP_OFF = I915_READ(PP_OFF_DELAYS);
+ dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL);
+ dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR);
+ dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+ dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
+ BACKLIGHT_DUTY_CYCLE_MASK);
+
+ /*
+ * If the light is off at server startup, just make it full brightness
+ */
+ if (dev_priv->backlight_duty_cycle == 0)
+ dev_priv->backlight_duty_cycle =
+ intel_lvds_get_max_backlight(dev);
+}
+
+static void intel_lvds_restore(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL);
+ I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON);
+ I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF);
+ I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR);
+ I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL);
+ if (dev_priv->savePP_CONTROL & POWER_TARGET_ON)
+ intel_lvds_set_power(dev, true);
+ else
+ intel_lvds_set_power(dev, false);
+}
+
+static int intel_lvds_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_display_mode *fixed_mode = dev_priv->panel_fixed_mode;
+
+ if (fixed_mode) {
+ if (mode->hdisplay > fixed_mode->hdisplay)
+ return MODE_PANEL;
+ if (mode->vdisplay > fixed_mode->vdisplay)
+ return MODE_PANEL;
+ }
+
+ return MODE_OK;
+}
+
+static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+ struct drm_encoder *tmp_encoder;
+
+ /* Should never happen!! */
+ if (!IS_I965G(dev) && intel_crtc->pipe == 0) {
+ printk(KERN_ERR "Can't support LVDS on pipe A\n");
+ return false;
+ }
+
+ /* Should never happen!! */
+ list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, head) {
+ if (tmp_encoder != encoder && tmp_encoder->crtc == encoder->crtc) {
+ printk(KERN_ERR "Can't enable LVDS and another "
+ "encoder on the same pipe\n");
+ return false;
+ }
+ }
+
+ /*
+ * If we have timings from the BIOS for the panel, put them in
+ * to the adjusted mode. The CRTC will be set up for this mode,
+ * with the panel scaling set up to source from the H/VDisplay
+ * of the original mode.
+ */
+ if (dev_priv->panel_fixed_mode != NULL) {
+ adjusted_mode->hdisplay = dev_priv->panel_fixed_mode->hdisplay;
+ adjusted_mode->hsync_start =
+ dev_priv->panel_fixed_mode->hsync_start;
+ adjusted_mode->hsync_end =
+ dev_priv->panel_fixed_mode->hsync_end;
+ adjusted_mode->htotal = dev_priv->panel_fixed_mode->htotal;
+ adjusted_mode->vdisplay = dev_priv->panel_fixed_mode->vdisplay;
+ adjusted_mode->vsync_start =
+ dev_priv->panel_fixed_mode->vsync_start;
+ adjusted_mode->vsync_end =
+ dev_priv->panel_fixed_mode->vsync_end;
+ adjusted_mode->vtotal = dev_priv->panel_fixed_mode->vtotal;
+ adjusted_mode->clock = dev_priv->panel_fixed_mode->clock;
+ drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
+ }
+
+ /*
+ * XXX: It would be nice to support lower refresh rates on the
+ * panels to reduce power consumption, and perhaps match the
+ * user's requested refresh rate.
+ */
+
+ return true;
+}
+
+static void intel_lvds_prepare(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+ dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
+ BACKLIGHT_DUTY_CYCLE_MASK);
+
+ intel_lvds_set_power(dev, false);
+}
+
+static void intel_lvds_commit( struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->backlight_duty_cycle == 0)
+ dev_priv->backlight_duty_cycle =
+ intel_lvds_get_max_backlight(dev);
+
+ intel_lvds_set_power(dev, true);
+}
+
+static void intel_lvds_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+ u32 pfit_control;
+
+ /*
+ * The LVDS pin pair will already have been turned on in the
+ * intel_crtc_mode_set since it has a large impact on the DPLL
+ * settings.
+ */
+
+ /*
+ * Enable automatic panel scaling so that non-native modes fill the
+ * screen. Should be enabled before the pipe is enabled, according to
+ * register description and PRM.
+ */
+ if (mode->hdisplay != adjusted_mode->hdisplay ||
+ mode->vdisplay != adjusted_mode->vdisplay)
+ pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE |
+ HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
+ HORIZ_INTERP_BILINEAR);
+ else
+ pfit_control = 0;
+
+ if (!IS_I965G(dev)) {
+ if (dev_priv->panel_wants_dither)
+ pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+ }
+ else
+ pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT;
+
+ I915_WRITE(PFIT_CONTROL, pfit_control);
+}
+
+/**
+ * Detect the LVDS connection.
+ *
+ * This always returns CONNECTOR_STATUS_CONNECTED. This connector should only have
+ * been set up if the LVDS was actually connected anyway.
+ */
+static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector)
+{
+ return connector_status_connected;
+}
+
+/**
+ * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
+ */
+static int intel_lvds_get_modes(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret = 0;
+
+ ret = intel_ddc_get_modes(intel_output);
+
+ if (ret)
+ return ret;
+
+ /* Didn't get an EDID, so
+ * Set wide sync ranges so we get all modes
+ * handed to valid_mode for checking
+ */
+ connector->display_info.min_vfreq = 0;
+ connector->display_info.max_vfreq = 200;
+ connector->display_info.min_hfreq = 0;
+ connector->display_info.max_hfreq = 200;
+
+ if (dev_priv->panel_fixed_mode != NULL) {
+ struct drm_display_mode *mode;
+
+ mutex_unlock(&dev->mode_config.mutex);
+ mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
+ drm_mode_probed_add(connector, mode);
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * intel_lvds_destroy - unregister and free LVDS structures
+ * @connector: connector to free
+ *
+ * Unregister the DDC bus for this connector then free the driver private
+ * structure.
+ */
+static void intel_lvds_destroy(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
+ .dpms = intel_lvds_dpms,
+ .mode_fixup = intel_lvds_mode_fixup,
+ .prepare = intel_lvds_prepare,
+ .mode_set = intel_lvds_mode_set,
+ .commit = intel_lvds_commit,
+};
+
+static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
+ .get_modes = intel_lvds_get_modes,
+ .mode_valid = intel_lvds_mode_valid,
+ .best_encoder = intel_best_encoder,
+};
+
+static const struct drm_connector_funcs intel_lvds_connector_funcs = {
+ .save = intel_lvds_save,
+ .restore = intel_lvds_restore,
+ .detect = intel_lvds_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = intel_lvds_destroy,
+};
+
+
+static void intel_lvds_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
+ .destroy = intel_lvds_enc_destroy,
+};
+
+
+
+/**
+ * intel_lvds_init - setup LVDS connectors on this device
+ * @dev: drm device
+ *
+ * Create the connector, register the LVDS DDC bus, and try to figure out what
+ * modes we can display on the LVDS panel (if present).
+ */
+void intel_lvds_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+ struct drm_display_mode *scan; /* *modes, *bios_mode; */
+ struct drm_crtc *crtc;
+ u32 lvds;
+ int pipe;
+
+ intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output) {
+ return;
+ }
+
+ connector = &intel_output->base;
+ encoder = &intel_output->enc;
+ drm_connector_init(dev, &intel_output->base, &intel_lvds_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_lvds_enc_funcs,
+ DRM_MODE_ENCODER_LVDS);
+
+ drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
+ intel_output->type = INTEL_OUTPUT_LVDS;
+
+ drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs);
+ drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs);
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+
+ /*
+ * LVDS discovery:
+ * 1) check for EDID on DDC
+ * 2) check for VBT data
+ * 3) check to see if LVDS is already on
+ * if none of the above, no panel
+ * 4) make sure lid is open
+ * if closed, act like it's not there for now
+ */
+
+ /* Set up the DDC bus. */
+ intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C");
+ if (!intel_output->ddc_bus) {
+ dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
+ "failed.\n");
+ goto failed;
+ }
+
+ /*
+ * Attempt to get the fixed panel mode from DDC. Assume that the
+ * preferred mode is the right one.
+ */
+ intel_ddc_get_modes(intel_output);
+
+ list_for_each_entry(scan, &connector->probed_modes, head) {
+ mutex_lock(&dev->mode_config.mutex);
+ if (scan->type & DRM_MODE_TYPE_PREFERRED) {
+ dev_priv->panel_fixed_mode =
+ drm_mode_duplicate(dev, scan);
+ mutex_unlock(&dev->mode_config.mutex);
+ goto out; /* FIXME: check for quirks */
+ }
+ mutex_unlock(&dev->mode_config.mutex);
+ }
+
+ /* Failed to get EDID, what about VBT? */
+ if (dev_priv->vbt_mode) {
+ mutex_lock(&dev->mode_config.mutex);
+ dev_priv->panel_fixed_mode =
+ drm_mode_duplicate(dev, dev_priv->vbt_mode);
+ mutex_unlock(&dev->mode_config.mutex);
+ }
+
+ /*
+ * If we didn't get EDID, try checking if the panel is already turned
+ * on. If so, assume that whatever is currently programmed is the
+ * correct mode.
+ */
+ lvds = I915_READ(LVDS);
+ pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
+ crtc = intel_get_crtc_from_pipe(dev, pipe);
+
+ if (crtc && (lvds & LVDS_PORT_EN)) {
+ dev_priv->panel_fixed_mode = intel_crtc_mode_get(dev, crtc);
+ if (dev_priv->panel_fixed_mode) {
+ dev_priv->panel_fixed_mode->type |=
+ DRM_MODE_TYPE_PREFERRED;
+ goto out; /* FIXME: check for quirks */
+ }
+ }
+
+ /* If we still don't have a mode after all that, give up. */
+ if (!dev_priv->panel_fixed_mode)
+ goto failed;
+
+ /* FIXME: detect aopen & mac mini type stuff automatically? */
+ /*
+ * Blacklist machines with BIOSes that list an LVDS panel without
+ * actually having one.
+ */
+ if (IS_I945GM(dev)) {
+ /* aopen mini pc */
+ if (dev->pdev->subsystem_vendor == 0xa0a0)
+ goto failed;
+
+ if ((dev->pdev->subsystem_vendor == 0x8086) &&
+ (dev->pdev->subsystem_device == 0x7270)) {
+ /* It's a Mac Mini or Macbook Pro.
+ *
+ * Apple hardware is out to get us. The macbook pro
+ * has a real LVDS panel, but the mac mini does not,
+ * and they have the same device IDs. We'll
+ * distinguish by panel size, on the assumption
+ * that Apple isn't about to make any machines with an
+ * 800x600 display.
+ */
+
+ if (dev_priv->panel_fixed_mode != NULL &&
+ dev_priv->panel_fixed_mode->hdisplay == 800 &&
+ dev_priv->panel_fixed_mode->vdisplay == 600) {
+ DRM_DEBUG("Suspected Mac Mini, ignoring the LVDS\n");
+ goto failed;
+ }
+ }
+ }
+
+
+out:
+ drm_sysfs_connector_add(connector);
+ return;
+
+failed:
+ DRM_DEBUG("No LVDS modes found, disabling.\n");
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c
new file mode 100644
index 000000000000..e42019e5d661
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (c) 2007 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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/i2c.h>
+#include <linux/fb.h>
+#include "drmP.h"
+#include "intel_drv.h"
+
+/**
+ * intel_ddc_probe
+ *
+ */
+bool intel_ddc_probe(struct intel_output *intel_output)
+{
+ u8 out_buf[] = { 0x0, 0x0};
+ u8 buf[2];
+ int ret;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = 0x50,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = 0x50,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(&intel_output->ddc_bus->adapter, msgs, 2);
+ if (ret == 2)
+ return true;
+
+ return false;
+}
+
+/**
+ * intel_ddc_get_modes - get modelist from monitor
+ * @connector: DRM connector device to use
+ *
+ * Fetch the EDID information from @connector using the DDC bus.
+ */
+int intel_ddc_get_modes(struct intel_output *intel_output)
+{
+ struct edid *edid;
+ int ret = 0;
+
+ edid = drm_get_edid(&intel_output->base,
+ &intel_output->ddc_bus->adapter);
+ if (edid) {
+ drm_mode_connector_update_edid_property(&intel_output->base,
+ edid);
+ ret = drm_add_edid_modes(&intel_output->base, edid);
+ kfree(edid);
+ }
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
new file mode 100644
index 000000000000..fbbaa4f414a0
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -0,0 +1,1128 @@
+/*
+ * Copyright 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright © 2006-2007 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "intel_sdvo_regs.h"
+
+#undef SDVO_DEBUG
+
+struct intel_sdvo_priv {
+ struct intel_i2c_chan *i2c_bus;
+ int slaveaddr;
+ int output_device;
+
+ u16 active_outputs;
+
+ struct intel_sdvo_caps caps;
+ int pixel_clock_min, pixel_clock_max;
+
+ int save_sdvo_mult;
+ u16 save_active_outputs;
+ struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
+ struct intel_sdvo_dtd save_output_dtd[16];
+ u32 save_SDVOX;
+};
+
+/**
+ * Writes the SDVOB or SDVOC with the given value, but always writes both
+ * SDVOB and SDVOC to work around apparent hardware issues (according to
+ * comments in the BIOS).
+ */
+static void intel_sdvo_write_sdvox(struct intel_output *intel_output, u32 val)
+{
+ struct drm_device *dev = intel_output->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u32 bval = val, cval = val;
+ int i;
+
+ if (sdvo_priv->output_device == SDVOB) {
+ cval = I915_READ(SDVOC);
+ } else {
+ bval = I915_READ(SDVOB);
+ }
+ /*
+ * Write the registers twice for luck. Sometimes,
+ * writing them only once doesn't appear to 'stick'.
+ * The BIOS does this too. Yay, magic
+ */
+ for (i = 0; i < 2; i++)
+ {
+ I915_WRITE(SDVOB, bval);
+ I915_READ(SDVOB);
+ I915_WRITE(SDVOC, cval);
+ I915_READ(SDVOC);
+ }
+}
+
+static bool intel_sdvo_read_byte(struct intel_output *intel_output, u8 addr,
+ u8 *ch)
+{
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u8 out_buf[2];
+ u8 buf[2];
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = sdvo_priv->i2c_bus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = sdvo_priv->i2c_bus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if ((ret = i2c_transfer(&sdvo_priv->i2c_bus->adapter, msgs, 2)) == 2)
+ {
+ *ch = buf[0];
+ return true;
+ }
+
+ DRM_DEBUG("i2c transfer returned %d\n", ret);
+ return false;
+}
+
+static bool intel_sdvo_write_byte(struct intel_output *intel_output, int addr,
+ u8 ch)
+{
+ u8 out_buf[2];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = intel_output->i2c_bus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&intel_output->i2c_bus->adapter, msgs, 1) == 1)
+ {
+ return true;
+ }
+ return false;
+}
+
+#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd}
+/** Mapping of command numbers to names, for debug output */
+const static struct _sdvo_cmd_name {
+ u8 cmd;
+ char *name;
+} sdvo_cmd_names[] = {
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_RESOLUTION_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),
+};
+
+#define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
+#define SDVO_PRIV(output) ((struct intel_sdvo_priv *) (output)->dev_priv)
+
+#ifdef SDVO_DEBUG
+static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
+ void *args, int args_len)
+{
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int i;
+
+ DRM_DEBUG("%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd);
+ for (i = 0; i < args_len; i++)
+ printk("%02X ", ((u8 *)args)[i]);
+ for (; i < 8; i++)
+ printk(" ");
+ for (i = 0; i < sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); i++) {
+ if (cmd == sdvo_cmd_names[i].cmd) {
+ printk("(%s)", sdvo_cmd_names[i].name);
+ break;
+ }
+ }
+ if (i == sizeof(sdvo_cmd_names)/ sizeof(sdvo_cmd_names[0]))
+ printk("(%02X)",cmd);
+ printk("\n");
+}
+#else
+#define intel_sdvo_debug_write(o, c, a, l)
+#endif
+
+static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd,
+ void *args, int args_len)
+{
+ int i;
+
+ intel_sdvo_debug_write(intel_output, cmd, args, args_len);
+
+ for (i = 0; i < args_len; i++) {
+ intel_sdvo_write_byte(intel_output, SDVO_I2C_ARG_0 - i,
+ ((u8*)args)[i]);
+ }
+
+ intel_sdvo_write_byte(intel_output, SDVO_I2C_OPCODE, cmd);
+}
+
+#ifdef SDVO_DEBUG
+static const char *cmd_status_names[] = {
+ "Power on",
+ "Success",
+ "Not supported",
+ "Invalid arg",
+ "Pending",
+ "Target not specified",
+ "Scaling not supported"
+};
+
+static void intel_sdvo_debug_response(struct intel_output *intel_output,
+ void *response, int response_len,
+ u8 status)
+{
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+
+ DRM_DEBUG("%s: R: ", SDVO_NAME(sdvo_priv));
+ for (i = 0; i < response_len; i++)
+ printk("%02X ", ((u8 *)response)[i]);
+ for (; i < 8; i++)
+ printk(" ");
+ if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
+ printk("(%s)", cmd_status_names[status]);
+ else
+ printk("(??? %d)", status);
+ printk("\n");
+}
+#else
+#define intel_sdvo_debug_response(o, r, l, s)
+#endif
+
+static u8 intel_sdvo_read_response(struct intel_output *intel_output,
+ void *response, int response_len)
+{
+ int i;
+ u8 status;
+ u8 retry = 50;
+
+ while (retry--) {
+ /* Read the command response */
+ for (i = 0; i < response_len; i++) {
+ intel_sdvo_read_byte(intel_output,
+ SDVO_I2C_RETURN_0 + i,
+ &((u8 *)response)[i]);
+ }
+
+ /* read the return status */
+ intel_sdvo_read_byte(intel_output, SDVO_I2C_CMD_STATUS,
+ &status);
+
+ intel_sdvo_debug_response(intel_output, response, response_len,
+ status);
+ if (status != SDVO_CMD_STATUS_PENDING)
+ return status;
+
+ mdelay(50);
+ }
+
+ return status;
+}
+
+static int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode)
+{
+ if (mode->clock >= 100000)
+ return 1;
+ else if (mode->clock >= 50000)
+ return 2;
+ else
+ return 4;
+}
+
+/**
+ * Don't check status code from this as it switches the bus back to the
+ * SDVO chips which defeats the purpose of doing a bus switch in the first
+ * place.
+ */
+static void intel_sdvo_set_control_bus_switch(struct intel_output *intel_output,
+ u8 target)
+{
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_CONTROL_BUS_SWITCH, &target, 1);
+}
+
+static bool intel_sdvo_set_target_input(struct intel_output *intel_output, bool target_0, bool target_1)
+{
+ struct intel_sdvo_set_target_input_args targets = {0};
+ u8 status;
+
+ if (target_0 && target_1)
+ return SDVO_CMD_STATUS_NOTSUPP;
+
+ if (target_1)
+ targets.target_1 = 1;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_TARGET_INPUT, &targets,
+ sizeof(targets));
+
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+/**
+ * Return whether each input is trained.
+ *
+ * This function is making an assumption about the layout of the response,
+ * which should be checked against the docs.
+ */
+static bool intel_sdvo_get_trained_inputs(struct intel_output *intel_output, bool *input_1, bool *input_2)
+{
+ struct intel_sdvo_get_trained_inputs_response response;
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_TRAINED_INPUTS, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, sizeof(response));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ *input_1 = response.input0_trained;
+ *input_2 = response.input1_trained;
+ return true;
+}
+
+static bool intel_sdvo_get_active_outputs(struct intel_output *intel_output,
+ u16 *outputs)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_OUTPUTS, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, outputs, sizeof(*outputs));
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_set_active_outputs(struct intel_output *intel_output,
+ u16 outputs)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_OUTPUTS, &outputs,
+ sizeof(outputs));
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_set_encoder_power_state(struct intel_output *intel_output,
+ int mode)
+{
+ u8 status, state = SDVO_ENCODER_STATE_ON;
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ state = SDVO_ENCODER_STATE_ON;
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ state = SDVO_ENCODER_STATE_STANDBY;
+ break;
+ case DRM_MODE_DPMS_SUSPEND:
+ state = SDVO_ENCODER_STATE_SUSPEND;
+ break;
+ case DRM_MODE_DPMS_OFF:
+ state = SDVO_ENCODER_STATE_OFF;
+ break;
+ }
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ENCODER_POWER_STATE, &state,
+ sizeof(state));
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_get_input_pixel_clock_range(struct intel_output *intel_output,
+ int *clock_min,
+ int *clock_max)
+{
+ struct intel_sdvo_pixel_clock_range clocks;
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE,
+ NULL, 0);
+
+ status = intel_sdvo_read_response(intel_output, &clocks, sizeof(clocks));
+
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ /* Convert the values from units of 10 kHz to kHz. */
+ *clock_min = clocks.min * 10;
+ *clock_max = clocks.max * 10;
+
+ return true;
+}
+
+static bool intel_sdvo_set_target_output(struct intel_output *intel_output,
+ u16 outputs)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_TARGET_OUTPUT, &outputs,
+ sizeof(outputs));
+
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_get_timing(struct intel_output *intel_output, u8 cmd,
+ struct intel_sdvo_dtd *dtd)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, cmd, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &dtd->part1,
+ sizeof(dtd->part1));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ intel_sdvo_write_cmd(intel_output, cmd + 1, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &dtd->part2,
+ sizeof(dtd->part2));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_get_input_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_get_timing(intel_output,
+ SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_get_output_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_get_timing(intel_output,
+ SDVO_CMD_GET_OUTPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_set_timing(struct intel_output *intel_output, u8 cmd,
+ struct intel_sdvo_dtd *dtd)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, cmd, &dtd->part1, sizeof(dtd->part1));
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ intel_sdvo_write_cmd(intel_output, cmd + 1, &dtd->part2, sizeof(dtd->part2));
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_set_input_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_set_timing(intel_output,
+ SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_set_output_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_set_timing(intel_output,
+ SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd);
+}
+
+
+static int intel_sdvo_get_clock_rate_mult(struct intel_output *intel_output)
+{
+ u8 response, status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_CLOCK_RATE_MULT, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, 1);
+
+ if (status != SDVO_CMD_STATUS_SUCCESS) {
+ DRM_DEBUG("Couldn't get SDVO clock rate multiplier\n");
+ return SDVO_CLOCK_RATE_MULT_1X;
+ } else {
+ DRM_DEBUG("Current clock rate multiplier: %d\n", response);
+ }
+
+ return response;
+}
+
+static bool intel_sdvo_set_clock_rate_mult(struct intel_output *intel_output, u8 val)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1);
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* Make the CRTC code factor in the SDVO pixel multiplier. The SDVO
+ * device will be told of the multiplier during mode_set.
+ */
+ adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode);
+ return true;
+}
+
+static void intel_sdvo_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u16 width, height;
+ u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len;
+ u16 h_sync_offset, v_sync_offset;
+ u32 sdvox;
+ struct intel_sdvo_dtd output_dtd;
+ int sdvo_pixel_multiply;
+
+ if (!mode)
+ return;
+
+ width = mode->crtc_hdisplay;
+ height = mode->crtc_vdisplay;
+
+ /* do some mode translations */
+ h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start;
+ h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+
+ v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start;
+ v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
+
+ h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start;
+ v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start;
+
+ output_dtd.part1.clock = mode->clock / 10;
+ output_dtd.part1.h_active = width & 0xff;
+ output_dtd.part1.h_blank = h_blank_len & 0xff;
+ output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) |
+ ((h_blank_len >> 8) & 0xf);
+ output_dtd.part1.v_active = height & 0xff;
+ output_dtd.part1.v_blank = v_blank_len & 0xff;
+ output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) |
+ ((v_blank_len >> 8) & 0xf);
+
+ output_dtd.part2.h_sync_off = h_sync_offset;
+ output_dtd.part2.h_sync_width = h_sync_len & 0xff;
+ output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 |
+ (v_sync_len & 0xf);
+ output_dtd.part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) |
+ ((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) |
+ ((v_sync_len & 0x30) >> 4);
+
+ output_dtd.part2.dtd_flags = 0x18;
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ output_dtd.part2.dtd_flags |= 0x2;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ output_dtd.part2.dtd_flags |= 0x4;
+
+ output_dtd.part2.sdvo_flags = 0;
+ output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0;
+ output_dtd.part2.reserved = 0;
+
+ /* Set the output timing to the screen */
+ intel_sdvo_set_target_output(intel_output, sdvo_priv->active_outputs);
+ intel_sdvo_set_output_timing(intel_output, &output_dtd);
+
+ /* Set the input timing to the screen. Assume always input 0. */
+ intel_sdvo_set_target_input(intel_output, true, false);
+
+ /* We would like to use i830_sdvo_create_preferred_input_timing() to
+ * provide the device with a timing it can support, if it supports that
+ * feature. However, presumably we would need to adjust the CRTC to
+ * output the preferred timing, and we don't support that currently.
+ */
+ intel_sdvo_set_input_timing(intel_output, &output_dtd);
+
+ switch (intel_sdvo_get_pixel_multiplier(mode)) {
+ case 1:
+ intel_sdvo_set_clock_rate_mult(intel_output,
+ SDVO_CLOCK_RATE_MULT_1X);
+ break;
+ case 2:
+ intel_sdvo_set_clock_rate_mult(intel_output,
+ SDVO_CLOCK_RATE_MULT_2X);
+ break;
+ case 4:
+ intel_sdvo_set_clock_rate_mult(intel_output,
+ SDVO_CLOCK_RATE_MULT_4X);
+ break;
+ }
+
+ /* Set the SDVO control regs. */
+ if (0/*IS_I965GM(dev)*/) {
+ sdvox = SDVO_BORDER_ENABLE;
+ } else {
+ sdvox = I915_READ(sdvo_priv->output_device);
+ switch (sdvo_priv->output_device) {
+ case SDVOB:
+ sdvox &= SDVOB_PRESERVE_MASK;
+ break;
+ case SDVOC:
+ sdvox &= SDVOC_PRESERVE_MASK;
+ break;
+ }
+ sdvox |= (9 << 19) | SDVO_BORDER_ENABLE;
+ }
+ if (intel_crtc->pipe == 1)
+ sdvox |= SDVO_PIPE_B_SELECT;
+
+ sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode);
+ if (IS_I965G(dev)) {
+ /* done in crtc_mode_set as the dpll_md reg must be written
+ early */
+ } else if (IS_I945G(dev) || IS_I945GM(dev)) {
+ /* done in crtc_mode_set as it lives inside the
+ dpll register */
+ } else {
+ sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT;
+ }
+
+ intel_sdvo_write_sdvox(intel_output, sdvox);
+}
+
+static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u32 temp;
+
+ if (mode != DRM_MODE_DPMS_ON) {
+ intel_sdvo_set_active_outputs(intel_output, 0);
+ if (0)
+ intel_sdvo_set_encoder_power_state(intel_output, mode);
+
+ if (mode == DRM_MODE_DPMS_OFF) {
+ temp = I915_READ(sdvo_priv->output_device);
+ if ((temp & SDVO_ENABLE) != 0) {
+ intel_sdvo_write_sdvox(intel_output, temp & ~SDVO_ENABLE);
+ }
+ }
+ } else {
+ bool input1, input2;
+ int i;
+ u8 status;
+
+ temp = I915_READ(sdvo_priv->output_device);
+ if ((temp & SDVO_ENABLE) == 0)
+ intel_sdvo_write_sdvox(intel_output, temp | SDVO_ENABLE);
+ for (i = 0; i < 2; i++)
+ intel_wait_for_vblank(dev);
+
+ status = intel_sdvo_get_trained_inputs(intel_output, &input1,
+ &input2);
+
+
+ /* Warn if the device reported failure to sync.
+ * A lot of SDVO devices fail to notify of sync, but it's
+ * a given it the status is a success, we succeeded.
+ */
+ if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
+ DRM_DEBUG("First %s output reported failure to sync\n",
+ SDVO_NAME(sdvo_priv));
+ }
+
+ if (0)
+ intel_sdvo_set_encoder_power_state(intel_output, mode);
+ intel_sdvo_set_active_outputs(intel_output, sdvo_priv->active_outputs);
+ }
+ return;
+}
+
+static void intel_sdvo_save(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int o;
+
+ sdvo_priv->save_sdvo_mult = intel_sdvo_get_clock_rate_mult(intel_output);
+ intel_sdvo_get_active_outputs(intel_output, &sdvo_priv->save_active_outputs);
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) {
+ intel_sdvo_set_target_input(intel_output, true, false);
+ intel_sdvo_get_input_timing(intel_output,
+ &sdvo_priv->save_input_dtd_1);
+ }
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) {
+ intel_sdvo_set_target_input(intel_output, false, true);
+ intel_sdvo_get_input_timing(intel_output,
+ &sdvo_priv->save_input_dtd_2);
+ }
+
+ for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++)
+ {
+ u16 this_output = (1 << o);
+ if (sdvo_priv->caps.output_flags & this_output)
+ {
+ intel_sdvo_set_target_output(intel_output, this_output);
+ intel_sdvo_get_output_timing(intel_output,
+ &sdvo_priv->save_output_dtd[o]);
+ }
+ }
+
+ sdvo_priv->save_SDVOX = I915_READ(sdvo_priv->output_device);
+}
+
+static void intel_sdvo_restore(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int o;
+ int i;
+ bool input1, input2;
+ u8 status;
+
+ intel_sdvo_set_active_outputs(intel_output, 0);
+
+ for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++)
+ {
+ u16 this_output = (1 << o);
+ if (sdvo_priv->caps.output_flags & this_output) {
+ intel_sdvo_set_target_output(intel_output, this_output);
+ intel_sdvo_set_output_timing(intel_output, &sdvo_priv->save_output_dtd[o]);
+ }
+ }
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) {
+ intel_sdvo_set_target_input(intel_output, true, false);
+ intel_sdvo_set_input_timing(intel_output, &sdvo_priv->save_input_dtd_1);
+ }
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) {
+ intel_sdvo_set_target_input(intel_output, false, true);
+ intel_sdvo_set_input_timing(intel_output, &sdvo_priv->save_input_dtd_2);
+ }
+
+ intel_sdvo_set_clock_rate_mult(intel_output, sdvo_priv->save_sdvo_mult);
+
+ I915_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX);
+
+ if (sdvo_priv->save_SDVOX & SDVO_ENABLE)
+ {
+ for (i = 0; i < 2; i++)
+ intel_wait_for_vblank(dev);
+ status = intel_sdvo_get_trained_inputs(intel_output, &input1, &input2);
+ if (status == SDVO_CMD_STATUS_SUCCESS && !input1)
+ DRM_DEBUG("First %s output reported failure to sync\n",
+ SDVO_NAME(sdvo_priv));
+ }
+
+ intel_sdvo_set_active_outputs(intel_output, sdvo_priv->save_active_outputs);
+}
+
+static int intel_sdvo_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ if (sdvo_priv->pixel_clock_min > mode->clock)
+ return MODE_CLOCK_LOW;
+
+ if (sdvo_priv->pixel_clock_max < mode->clock)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static bool intel_sdvo_get_capabilities(struct intel_output *intel_output, struct intel_sdvo_caps *caps)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_DEVICE_CAPS, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, caps, sizeof(*caps));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB)
+{
+ struct drm_connector *connector = NULL;
+ struct intel_output *iout = NULL;
+ struct intel_sdvo_priv *sdvo;
+
+ /* find the sdvo connector */
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ iout = to_intel_output(connector);
+
+ if (iout->type != INTEL_OUTPUT_SDVO)
+ continue;
+
+ sdvo = iout->dev_priv;
+
+ if (sdvo->output_device == SDVOB && sdvoB)
+ return connector;
+
+ if (sdvo->output_device == SDVOC && !sdvoB)
+ return connector;
+
+ }
+
+ return NULL;
+}
+
+int intel_sdvo_supports_hotplug(struct drm_connector *connector)
+{
+ u8 response[2];
+ u8 status;
+ struct intel_output *intel_output;
+ DRM_DEBUG("\n");
+
+ if (!connector)
+ return 0;
+
+ intel_output = to_intel_output(connector);
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, 2);
+
+ if (response[0] !=0)
+ return 1;
+
+ return 0;
+}
+
+void intel_sdvo_set_hotplug(struct drm_connector *connector, int on)
+{
+ u8 response[2];
+ u8 status;
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0);
+ intel_sdvo_read_response(intel_output, &response, 2);
+
+ if (on) {
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, 2);
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2);
+ } else {
+ response[0] = 0;
+ response[1] = 0;
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2);
+ }
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0);
+ intel_sdvo_read_response(intel_output, &response, 2);
+}
+
+static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connector)
+{
+ u8 response[2];
+ u8 status;
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, 2);
+
+ DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]);
+ if ((response[0] != 0) || (response[1] != 0))
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+}
+
+static int intel_sdvo_get_modes(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ /* set the bus switch and get the modes */
+ intel_sdvo_set_control_bus_switch(intel_output, SDVO_CONTROL_BUS_DDC2);
+ intel_ddc_get_modes(intel_output);
+
+ if (list_empty(&connector->probed_modes))
+ return 0;
+ return 1;
+}
+
+static void intel_sdvo_destroy(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ if (intel_output->i2c_bus)
+ intel_i2c_destroy(intel_output->i2c_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(intel_output);
+}
+
+static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = {
+ .dpms = intel_sdvo_dpms,
+ .mode_fixup = intel_sdvo_mode_fixup,
+ .prepare = intel_encoder_prepare,
+ .mode_set = intel_sdvo_mode_set,
+ .commit = intel_encoder_commit,
+};
+
+static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
+ .save = intel_sdvo_save,
+ .restore = intel_sdvo_restore,
+ .detect = intel_sdvo_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = intel_sdvo_destroy,
+};
+
+static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = {
+ .get_modes = intel_sdvo_get_modes,
+ .mode_valid = intel_sdvo_mode_valid,
+ .best_encoder = intel_best_encoder,
+};
+
+static void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_sdvo_enc_funcs = {
+ .destroy = intel_sdvo_enc_destroy,
+};
+
+
+void intel_sdvo_init(struct drm_device *dev, int output_device)
+{
+ struct drm_connector *connector;
+ struct intel_output *intel_output;
+ struct intel_sdvo_priv *sdvo_priv;
+ struct intel_i2c_chan *i2cbus = NULL;
+ int connector_type;
+ u8 ch[0x40];
+ int i;
+ int encoder_type, output_id;
+
+ intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL);
+ if (!intel_output) {
+ return;
+ }
+
+ connector = &intel_output->base;
+
+ drm_connector_init(dev, connector, &intel_sdvo_connector_funcs,
+ DRM_MODE_CONNECTOR_Unknown);
+ drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs);
+ sdvo_priv = (struct intel_sdvo_priv *)(intel_output + 1);
+ intel_output->type = INTEL_OUTPUT_SDVO;
+
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+
+ /* setup the DDC bus. */
+ if (output_device == SDVOB)
+ i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB");
+ else
+ i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC");
+
+ if (!i2cbus)
+ goto err_connector;
+
+ sdvo_priv->i2c_bus = i2cbus;
+
+ if (output_device == SDVOB) {
+ output_id = 1;
+ sdvo_priv->i2c_bus->slave_addr = 0x38;
+ } else {
+ output_id = 2;
+ sdvo_priv->i2c_bus->slave_addr = 0x39;
+ }
+
+ sdvo_priv->output_device = output_device;
+ intel_output->i2c_bus = i2cbus;
+ intel_output->dev_priv = sdvo_priv;
+
+
+ /* Read the regs to test if we can talk to the device */
+ for (i = 0; i < 0x40; i++) {
+ if (!intel_sdvo_read_byte(intel_output, i, &ch[i])) {
+ DRM_DEBUG("No SDVO device found on SDVO%c\n",
+ output_device == SDVOB ? 'B' : 'C');
+ goto err_i2c;
+ }
+ }
+
+ intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
+
+ memset(&sdvo_priv->active_outputs, 0, sizeof(sdvo_priv->active_outputs));
+
+ /* TODO, CVBS, SVID, YPRPB & SCART outputs. */
+ if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_DAC;
+ connector_type = DRM_MODE_CONNECTOR_VGA;
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_DAC;
+ connector_type = DRM_MODE_CONNECTOR_VGA;
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_TMDS;
+ connector_type = DRM_MODE_CONNECTOR_DVID;
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_TMDS;
+ connector_type = DRM_MODE_CONNECTOR_DVID;
+ }
+ else
+ {
+ unsigned char bytes[2];
+
+ memcpy (bytes, &sdvo_priv->caps.output_flags, 2);
+ DRM_DEBUG("%s: No active RGB or TMDS outputs (0x%02x%02x)\n",
+ SDVO_NAME(sdvo_priv),
+ bytes[0], bytes[1]);
+ goto err_i2c;
+ }
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_sdvo_enc_funcs, encoder_type);
+ drm_encoder_helper_add(&intel_output->enc, &intel_sdvo_helper_funcs);
+ connector->connector_type = connector_type;
+
+ drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
+ drm_sysfs_connector_add(connector);
+
+ /* Set the input timing to the screen. Assume always input 0. */
+ intel_sdvo_set_target_input(intel_output, true, false);
+
+ intel_sdvo_get_input_pixel_clock_range(intel_output,
+ &sdvo_priv->pixel_clock_min,
+ &sdvo_priv->pixel_clock_max);
+
+
+ DRM_DEBUG("%s device VID/DID: %02X:%02X.%02X, "
+ "clock range %dMHz - %dMHz, "
+ "input 1: %c, input 2: %c, "
+ "output 1: %c, output 2: %c\n",
+ SDVO_NAME(sdvo_priv),
+ sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id,
+ sdvo_priv->caps.device_rev_id,
+ sdvo_priv->pixel_clock_min / 1000,
+ sdvo_priv->pixel_clock_max / 1000,
+ (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N',
+ (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N',
+ /* check currently supported outputs */
+ sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N',
+ sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N');
+
+ intel_output->ddc_bus = i2cbus;
+
+ return;
+
+err_i2c:
+ intel_i2c_destroy(intel_output->i2c_bus);
+err_connector:
+ drm_connector_cleanup(connector);
+ kfree(intel_output);
+
+ return;
+}
diff --git a/drivers/gpu/drm/i915/intel_sdvo_regs.h b/drivers/gpu/drm/i915/intel_sdvo_regs.h
new file mode 100644
index 000000000000..861a43f8693c
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_sdvo_regs.h
@@ -0,0 +1,327 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+
+/**
+ * @file SDVO command definitions and structures.
+ */
+
+#define SDVO_OUTPUT_FIRST (0)
+#define SDVO_OUTPUT_TMDS0 (1 << 0)
+#define SDVO_OUTPUT_RGB0 (1 << 1)
+#define SDVO_OUTPUT_CVBS0 (1 << 2)
+#define SDVO_OUTPUT_SVID0 (1 << 3)
+#define SDVO_OUTPUT_YPRPB0 (1 << 4)
+#define SDVO_OUTPUT_SCART0 (1 << 5)
+#define SDVO_OUTPUT_LVDS0 (1 << 6)
+#define SDVO_OUTPUT_TMDS1 (1 << 8)
+#define SDVO_OUTPUT_RGB1 (1 << 9)
+#define SDVO_OUTPUT_CVBS1 (1 << 10)
+#define SDVO_OUTPUT_SVID1 (1 << 11)
+#define SDVO_OUTPUT_YPRPB1 (1 << 12)
+#define SDVO_OUTPUT_SCART1 (1 << 13)
+#define SDVO_OUTPUT_LVDS1 (1 << 14)
+#define SDVO_OUTPUT_LAST (14)
+
+struct intel_sdvo_caps {
+ u8 vendor_id;
+ u8 device_id;
+ u8 device_rev_id;
+ u8 sdvo_version_major;
+ u8 sdvo_version_minor;
+ unsigned int sdvo_inputs_mask:2;
+ unsigned int smooth_scaling:1;
+ unsigned int sharp_scaling:1;
+ unsigned int up_scaling:1;
+ unsigned int down_scaling:1;
+ unsigned int stall_support:1;
+ unsigned int pad:1;
+ u16 output_flags;
+} __attribute__((packed));
+
+/** This matches the EDID DTD structure, more or less */
+struct intel_sdvo_dtd {
+ struct {
+ u16 clock; /**< pixel clock, in 10kHz units */
+ u8 h_active; /**< lower 8 bits (pixels) */
+ u8 h_blank; /**< lower 8 bits (pixels) */
+ u8 h_high; /**< upper 4 bits each h_active, h_blank */
+ u8 v_active; /**< lower 8 bits (lines) */
+ u8 v_blank; /**< lower 8 bits (lines) */
+ u8 v_high; /**< upper 4 bits each v_active, v_blank */
+ } part1;
+
+ struct {
+ u8 h_sync_off; /**< lower 8 bits, from hblank start */
+ u8 h_sync_width; /**< lower 8 bits (pixels) */
+ /** lower 4 bits each vsync offset, vsync width */
+ u8 v_sync_off_width;
+ /**
+ * 2 high bits of hsync offset, 2 high bits of hsync width,
+ * bits 4-5 of vsync offset, and 2 high bits of vsync width.
+ */
+ u8 sync_off_width_high;
+ u8 dtd_flags;
+ u8 sdvo_flags;
+ /** bits 6-7 of vsync offset at bits 6-7 */
+ u8 v_sync_off_high;
+ u8 reserved;
+ } part2;
+} __attribute__((packed));
+
+struct intel_sdvo_pixel_clock_range {
+ u16 min; /**< pixel clock, in 10kHz units */
+ u16 max; /**< pixel clock, in 10kHz units */
+} __attribute__((packed));
+
+struct intel_sdvo_preferred_input_timing_args {
+ u16 clock;
+ u16 width;
+ u16 height;
+} __attribute__((packed));
+
+/* I2C registers for SDVO */
+#define SDVO_I2C_ARG_0 0x07
+#define SDVO_I2C_ARG_1 0x06
+#define SDVO_I2C_ARG_2 0x05
+#define SDVO_I2C_ARG_3 0x04
+#define SDVO_I2C_ARG_4 0x03
+#define SDVO_I2C_ARG_5 0x02
+#define SDVO_I2C_ARG_6 0x01
+#define SDVO_I2C_ARG_7 0x00
+#define SDVO_I2C_OPCODE 0x08
+#define SDVO_I2C_CMD_STATUS 0x09
+#define SDVO_I2C_RETURN_0 0x0a
+#define SDVO_I2C_RETURN_1 0x0b
+#define SDVO_I2C_RETURN_2 0x0c
+#define SDVO_I2C_RETURN_3 0x0d
+#define SDVO_I2C_RETURN_4 0x0e
+#define SDVO_I2C_RETURN_5 0x0f
+#define SDVO_I2C_RETURN_6 0x10
+#define SDVO_I2C_RETURN_7 0x11
+#define SDVO_I2C_VENDOR_BEGIN 0x20
+
+/* Status results */
+#define SDVO_CMD_STATUS_POWER_ON 0x0
+#define SDVO_CMD_STATUS_SUCCESS 0x1
+#define SDVO_CMD_STATUS_NOTSUPP 0x2
+#define SDVO_CMD_STATUS_INVALID_ARG 0x3
+#define SDVO_CMD_STATUS_PENDING 0x4
+#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED 0x5
+#define SDVO_CMD_STATUS_SCALING_NOT_SUPP 0x6
+
+/* SDVO commands, argument/result registers */
+
+#define SDVO_CMD_RESET 0x01
+
+/** Returns a struct intel_sdvo_caps */
+#define SDVO_CMD_GET_DEVICE_CAPS 0x02
+
+#define SDVO_CMD_GET_FIRMWARE_REV 0x86
+# define SDVO_DEVICE_FIRMWARE_MINOR SDVO_I2C_RETURN_0
+# define SDVO_DEVICE_FIRMWARE_MAJOR SDVO_I2C_RETURN_1
+# define SDVO_DEVICE_FIRMWARE_PATCH SDVO_I2C_RETURN_2
+
+/**
+ * Reports which inputs are trained (managed to sync).
+ *
+ * Devices must have trained within 2 vsyncs of a mode change.
+ */
+#define SDVO_CMD_GET_TRAINED_INPUTS 0x03
+struct intel_sdvo_get_trained_inputs_response {
+ unsigned int input0_trained:1;
+ unsigned int input1_trained:1;
+ unsigned int pad:6;
+} __attribute__((packed));
+
+/** Returns a struct intel_sdvo_output_flags of active outputs. */
+#define SDVO_CMD_GET_ACTIVE_OUTPUTS 0x04
+
+/**
+ * Sets the current set of active outputs.
+ *
+ * Takes a struct intel_sdvo_output_flags. Must be preceded by a SET_IN_OUT_MAP
+ * on multi-output devices.
+ */
+#define SDVO_CMD_SET_ACTIVE_OUTPUTS 0x05
+
+/**
+ * Returns the current mapping of SDVO inputs to outputs on the device.
+ *
+ * Returns two struct intel_sdvo_output_flags structures.
+ */
+#define SDVO_CMD_GET_IN_OUT_MAP 0x06
+
+/**
+ * Sets the current mapping of SDVO inputs to outputs on the device.
+ *
+ * Takes two struct i380_sdvo_output_flags structures.
+ */
+#define SDVO_CMD_SET_IN_OUT_MAP 0x07
+
+/**
+ * Returns a struct intel_sdvo_output_flags of attached displays.
+ */
+#define SDVO_CMD_GET_ATTACHED_DISPLAYS 0x0b
+
+/**
+ * Returns a struct intel_sdvo_ouptut_flags of displays supporting hot plugging.
+ */
+#define SDVO_CMD_GET_HOT_PLUG_SUPPORT 0x0c
+
+/**
+ * Takes a struct intel_sdvo_output_flags.
+ */
+#define SDVO_CMD_SET_ACTIVE_HOT_PLUG 0x0d
+
+/**
+ * Returns a struct intel_sdvo_output_flags of displays with hot plug
+ * interrupts enabled.
+ */
+#define SDVO_CMD_GET_ACTIVE_HOT_PLUG 0x0e
+
+#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE 0x0f
+struct intel_sdvo_get_interrupt_event_source_response {
+ u16 interrupt_status;
+ unsigned int ambient_light_interrupt:1;
+ unsigned int pad:7;
+} __attribute__((packed));
+
+/**
+ * Selects which input is affected by future input commands.
+ *
+ * Commands affected include SET_INPUT_TIMINGS_PART[12],
+ * GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12],
+ * GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS.
+ */
+#define SDVO_CMD_SET_TARGET_INPUT 0x10
+struct intel_sdvo_set_target_input_args {
+ unsigned int target_1:1;
+ unsigned int pad:7;
+} __attribute__((packed));
+
+/**
+ * Takes a struct intel_sdvo_output_flags of which outputs are targetted by
+ * future output commands.
+ *
+ * Affected commands inclue SET_OUTPUT_TIMINGS_PART[12],
+ * GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE.
+ */
+#define SDVO_CMD_SET_TARGET_OUTPUT 0x11
+
+#define SDVO_CMD_GET_INPUT_TIMINGS_PART1 0x12
+#define SDVO_CMD_GET_INPUT_TIMINGS_PART2 0x13
+#define SDVO_CMD_SET_INPUT_TIMINGS_PART1 0x14
+#define SDVO_CMD_SET_INPUT_TIMINGS_PART2 0x15
+#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1 0x16
+#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2 0x17
+#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1 0x18
+#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2 0x19
+/* Part 1 */
+# define SDVO_DTD_CLOCK_LOW SDVO_I2C_ARG_0
+# define SDVO_DTD_CLOCK_HIGH SDVO_I2C_ARG_1
+# define SDVO_DTD_H_ACTIVE SDVO_I2C_ARG_2
+# define SDVO_DTD_H_BLANK SDVO_I2C_ARG_3
+# define SDVO_DTD_H_HIGH SDVO_I2C_ARG_4
+# define SDVO_DTD_V_ACTIVE SDVO_I2C_ARG_5
+# define SDVO_DTD_V_BLANK SDVO_I2C_ARG_6
+# define SDVO_DTD_V_HIGH SDVO_I2C_ARG_7
+/* Part 2 */
+# define SDVO_DTD_HSYNC_OFF SDVO_I2C_ARG_0
+# define SDVO_DTD_HSYNC_WIDTH SDVO_I2C_ARG_1
+# define SDVO_DTD_VSYNC_OFF_WIDTH SDVO_I2C_ARG_2
+# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH SDVO_I2C_ARG_3
+# define SDVO_DTD_DTD_FLAGS SDVO_I2C_ARG_4
+# define SDVO_DTD_DTD_FLAG_INTERLACED (1 << 7)
+# define SDVO_DTD_DTD_FLAG_STEREO_MASK (3 << 5)
+# define SDVO_DTD_DTD_FLAG_INPUT_MASK (3 << 3)
+# define SDVO_DTD_DTD_FLAG_SYNC_MASK (3 << 1)
+# define SDVO_DTD_SDVO_FLAS SDVO_I2C_ARG_5
+# define SDVO_DTD_SDVO_FLAG_STALL (1 << 7)
+# define SDVO_DTD_SDVO_FLAG_CENTERED (0 << 6)
+# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT (1 << 6)
+# define SDVO_DTD_SDVO_FLAG_SCALING_MASK (3 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_NONE (0 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP (1 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH (2 << 4)
+# define SDVO_DTD_VSYNC_OFF_HIGH SDVO_I2C_ARG_6
+
+/**
+ * Generates a DTD based on the given width, height, and flags.
+ *
+ * This will be supported by any device supporting scaling or interlaced
+ * modes.
+ */
+#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING 0x1a
+# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW SDVO_I2C_ARG_0
+# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH SDVO_I2C_ARG_1
+# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW SDVO_I2C_ARG_2
+# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH SDVO_I2C_ARG_3
+# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW SDVO_I2C_ARG_4
+# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH SDVO_I2C_ARG_5
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS SDVO_I2C_ARG_6
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED (1 << 0)
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED (1 << 1)
+
+#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1 0x1b
+#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2 0x1c
+
+/** Returns a struct intel_sdvo_pixel_clock_range */
+#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE 0x1d
+/** Returns a struct intel_sdvo_pixel_clock_range */
+#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE 0x1e
+
+/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */
+#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS 0x1f
+
+/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
+#define SDVO_CMD_GET_CLOCK_RATE_MULT 0x20
+/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
+#define SDVO_CMD_SET_CLOCK_RATE_MULT 0x21
+# define SDVO_CLOCK_RATE_MULT_1X (1 << 0)
+# define SDVO_CLOCK_RATE_MULT_2X (1 << 1)
+# define SDVO_CLOCK_RATE_MULT_4X (1 << 3)
+
+#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27
+
+#define SDVO_CMD_GET_TV_FORMAT 0x28
+
+#define SDVO_CMD_SET_TV_FORMAT 0x29
+
+#define SDVO_CMD_GET_SUPPORTED_POWER_STATES 0x2a
+#define SDVO_CMD_GET_ENCODER_POWER_STATE 0x2b
+#define SDVO_CMD_SET_ENCODER_POWER_STATE 0x2c
+# define SDVO_ENCODER_STATE_ON (1 << 0)
+# define SDVO_ENCODER_STATE_STANDBY (1 << 1)
+# define SDVO_ENCODER_STATE_SUSPEND (1 << 2)
+# define SDVO_ENCODER_STATE_OFF (1 << 3)
+
+#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT 0x93
+
+#define SDVO_CMD_SET_CONTROL_BUS_SWITCH 0x7a
+# define SDVO_CONTROL_BUS_PROM 0x0
+# define SDVO_CONTROL_BUS_DDC1 0x1
+# define SDVO_CONTROL_BUS_DDC2 0x2
+# define SDVO_CONTROL_BUS_DDC3 0x3
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
new file mode 100644
index 000000000000..fbb35dc56f5c
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -0,0 +1,1725 @@
+/*
+ * Copyright © 2006-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+/** @file
+ * Integrated TV-out support for the 915GM and 945GM.
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_edid.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+enum tv_margin {
+ TV_MARGIN_LEFT, TV_MARGIN_TOP,
+ TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM
+};
+
+/** Private structure for the integrated TV support */
+struct intel_tv_priv {
+ int type;
+ char *tv_format;
+ int margin[4];
+ u32 save_TV_H_CTL_1;
+ u32 save_TV_H_CTL_2;
+ u32 save_TV_H_CTL_3;
+ u32 save_TV_V_CTL_1;
+ u32 save_TV_V_CTL_2;
+ u32 save_TV_V_CTL_3;
+ u32 save_TV_V_CTL_4;
+ u32 save_TV_V_CTL_5;
+ u32 save_TV_V_CTL_6;
+ u32 save_TV_V_CTL_7;
+ u32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3;
+
+ u32 save_TV_CSC_Y;
+ u32 save_TV_CSC_Y2;
+ u32 save_TV_CSC_U;
+ u32 save_TV_CSC_U2;
+ u32 save_TV_CSC_V;
+ u32 save_TV_CSC_V2;
+ u32 save_TV_CLR_KNOBS;
+ u32 save_TV_CLR_LEVEL;
+ u32 save_TV_WIN_POS;
+ u32 save_TV_WIN_SIZE;
+ u32 save_TV_FILTER_CTL_1;
+ u32 save_TV_FILTER_CTL_2;
+ u32 save_TV_FILTER_CTL_3;
+
+ u32 save_TV_H_LUMA[60];
+ u32 save_TV_H_CHROMA[60];
+ u32 save_TV_V_LUMA[43];
+ u32 save_TV_V_CHROMA[43];
+
+ u32 save_TV_DAC;
+ u32 save_TV_CTL;
+};
+
+struct video_levels {
+ int blank, black, burst;
+};
+
+struct color_conversion {
+ u16 ry, gy, by, ay;
+ u16 ru, gu, bu, au;
+ u16 rv, gv, bv, av;
+};
+
+static const u32 filter_table[] = {
+ 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
+ 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
+ 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
+ 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
+ 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
+ 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
+ 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
+ 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
+ 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
+ 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
+ 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
+ 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
+ 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
+ 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
+ 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
+ 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
+ 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
+ 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
+ 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
+ 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
+ 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
+ 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
+ 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
+ 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
+ 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
+ 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
+ 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
+ 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
+ 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
+ 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
+ 0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
+ 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
+ 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
+ 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
+ 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
+ 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
+ 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
+ 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
+ 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
+ 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
+ 0x28003100, 0x28002F00, 0x00003100, 0x36403000,
+ 0x2D002CC0, 0x30003640, 0x2D0036C0,
+ 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
+ 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
+ 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
+ 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
+ 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
+ 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
+ 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
+ 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
+ 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
+ 0x28003100, 0x28002F00, 0x00003100,
+};
+
+/*
+ * Color conversion values have 3 separate fixed point formats:
+ *
+ * 10 bit fields (ay, au)
+ * 1.9 fixed point (b.bbbbbbbbb)
+ * 11 bit fields (ry, by, ru, gu, gv)
+ * exp.mantissa (ee.mmmmmmmmm)
+ * ee = 00 = 10^-1 (0.mmmmmmmmm)
+ * ee = 01 = 10^-2 (0.0mmmmmmmmm)
+ * ee = 10 = 10^-3 (0.00mmmmmmmmm)
+ * ee = 11 = 10^-4 (0.000mmmmmmmmm)
+ * 12 bit fields (gy, rv, bu)
+ * exp.mantissa (eee.mmmmmmmmm)
+ * eee = 000 = 10^-1 (0.mmmmmmmmm)
+ * eee = 001 = 10^-2 (0.0mmmmmmmmm)
+ * eee = 010 = 10^-3 (0.00mmmmmmmmm)
+ * eee = 011 = 10^-4 (0.000mmmmmmmmm)
+ * eee = 100 = reserved
+ * eee = 101 = reserved
+ * eee = 110 = reserved
+ * eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation)
+ *
+ * Saturation and contrast are 8 bits, with their own representation:
+ * 8 bit field (saturation, contrast)
+ * exp.mantissa (ee.mmmmmm)
+ * ee = 00 = 10^-1 (0.mmmmmm)
+ * ee = 01 = 10^0 (m.mmmmm)
+ * ee = 10 = 10^1 (mm.mmmm)
+ * ee = 11 = 10^2 (mmm.mmm)
+ *
+ * Simple conversion function:
+ *
+ * static u32
+ * float_to_csc_11(float f)
+ * {
+ * u32 exp;
+ * u32 mant;
+ * u32 ret;
+ *
+ * if (f < 0)
+ * f = -f;
+ *
+ * if (f >= 1) {
+ * exp = 0x7;
+ * mant = 1 << 8;
+ * } else {
+ * for (exp = 0; exp < 3 && f < 0.5; exp++)
+ * f *= 2.0;
+ * mant = (f * (1 << 9) + 0.5);
+ * if (mant >= (1 << 9))
+ * mant = (1 << 9) - 1;
+ * }
+ * ret = (exp << 9) | mant;
+ * return ret;
+ * }
+ */
+
+/*
+ * Behold, magic numbers! If we plant them they might grow a big
+ * s-video cable to the sky... or something.
+ *
+ * Pre-converted to appropriate hex value.
+ */
+
+/*
+ * PAL & NTSC values for composite & s-video connections
+ */
+static const struct color_conversion ntsc_m_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
+ .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00,
+ .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00,
+};
+
+static const struct video_levels ntsc_m_levels_composite = {
+ .blank = 225, .black = 267, .burst = 113,
+};
+
+static const struct color_conversion ntsc_m_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134,
+ .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00,
+ .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00,
+};
+
+static const struct video_levels ntsc_m_levels_svideo = {
+ .blank = 266, .black = 316, .burst = 133,
+};
+
+static const struct color_conversion ntsc_j_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119,
+ .ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0f00,
+ .rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0f00,
+};
+
+static const struct video_levels ntsc_j_levels_composite = {
+ .blank = 225, .black = 225, .burst = 113,
+};
+
+static const struct color_conversion ntsc_j_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c,
+ .ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0f00,
+ .rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0f00,
+};
+
+static const struct video_levels ntsc_j_levels_svideo = {
+ .blank = 266, .black = 266, .burst = 133,
+};
+
+static const struct color_conversion pal_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113,
+ .ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0f00,
+ .rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0f00,
+};
+
+static const struct video_levels pal_levels_composite = {
+ .blank = 237, .black = 237, .burst = 118,
+};
+
+static const struct color_conversion pal_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
+ .ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0f00,
+ .rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0f00,
+};
+
+static const struct video_levels pal_levels_svideo = {
+ .blank = 280, .black = 280, .burst = 139,
+};
+
+static const struct color_conversion pal_m_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
+ .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00,
+ .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00,
+};
+
+static const struct video_levels pal_m_levels_composite = {
+ .blank = 225, .black = 267, .burst = 113,
+};
+
+static const struct color_conversion pal_m_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134,
+ .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00,
+ .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00,
+};
+
+static const struct video_levels pal_m_levels_svideo = {
+ .blank = 266, .black = 316, .burst = 133,
+};
+
+static const struct color_conversion pal_n_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
+ .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00,
+ .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00,
+};
+
+static const struct video_levels pal_n_levels_composite = {
+ .blank = 225, .black = 267, .burst = 118,
+};
+
+static const struct color_conversion pal_n_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134,
+ .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00,
+ .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00,
+};
+
+static const struct video_levels pal_n_levels_svideo = {
+ .blank = 266, .black = 316, .burst = 139,
+};
+
+/*
+ * Component connections
+ */
+static const struct color_conversion sdtv_csc_yprpb = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0146,
+ .ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0f00,
+ .rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0f00,
+};
+
+static const struct color_conversion sdtv_csc_rgb = {
+ .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
+ .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
+ .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
+};
+
+static const struct color_conversion hdtv_csc_yprpb = {
+ .ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0146,
+ .ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0f00,
+ .rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0f00,
+};
+
+static const struct color_conversion hdtv_csc_rgb = {
+ .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
+ .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
+ .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
+};
+
+static const struct video_levels component_levels = {
+ .blank = 279, .black = 279, .burst = 0,
+};
+
+
+struct tv_mode {
+ char *name;
+ int clock;
+ int refresh; /* in millihertz (for precision) */
+ u32 oversample;
+ int hsync_end, hblank_start, hblank_end, htotal;
+ bool progressive, trilevel_sync, component_only;
+ int vsync_start_f1, vsync_start_f2, vsync_len;
+ bool veq_ena;
+ int veq_start_f1, veq_start_f2, veq_len;
+ int vi_end_f1, vi_end_f2, nbr_end;
+ bool burst_ena;
+ int hburst_start, hburst_len;
+ int vburst_start_f1, vburst_end_f1;
+ int vburst_start_f2, vburst_end_f2;
+ int vburst_start_f3, vburst_end_f3;
+ int vburst_start_f4, vburst_end_f4;
+ /*
+ * subcarrier programming
+ */
+ int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc;
+ u32 sc_reset;
+ bool pal_burst;
+ /*
+ * blank/black levels
+ */
+ const struct video_levels *composite_levels, *svideo_levels;
+ const struct color_conversion *composite_color, *svideo_color;
+ const u32 *filter_table;
+ int max_srcw;
+};
+
+
+/*
+ * Sub carrier DDA
+ *
+ * I think this works as follows:
+ *
+ * subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
+ *
+ * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
+ *
+ * So,
+ * dda1_ideal = subcarrier/pixel * 4096
+ * dda1_inc = floor (dda1_ideal)
+ * dda2 = dda1_ideal - dda1_inc
+ *
+ * then pick a ratio for dda2 that gives the closest approximation. If
+ * you can't get close enough, you can play with dda3 as well. This
+ * seems likely to happen when dda2 is small as the jumps would be larger
+ *
+ * To invert this,
+ *
+ * pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
+ *
+ * The constants below were all computed using a 107.520MHz clock
+ */
+
+/**
+ * Register programming values for TV modes.
+ *
+ * These values account for -1s required.
+ */
+
+const static struct tv_mode tv_modes[] = {
+ {
+ .name = "NTSC-M",
+ .clock = 107520,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+ /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
+
+ .hsync_end = 64, .hblank_end = 124,
+ .hblank_start = 836, .htotal = 857,
+
+ .progressive = false, .trilevel_sync = false,
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = true, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 20, .vi_end_f2 = 21,
+ .nbr_end = 240,
+
+ .burst_ena = true,
+ .hburst_start = 72, .hburst_len = 34,
+ .vburst_start_f1 = 9, .vburst_end_f1 = 240,
+ .vburst_start_f2 = 10, .vburst_end_f2 = 240,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 240,
+ .vburst_start_f4 = 10, .vburst_end_f4 = 240,
+
+ /* desired 3.5800000 actual 3.5800000 clock 107.52 */
+ .dda1_inc = 136,
+ .dda2_inc = 7624, .dda2_size = 20013,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_4,
+ .pal_burst = false,
+
+ .composite_levels = &ntsc_m_levels_composite,
+ .composite_color = &ntsc_m_csc_composite,
+ .svideo_levels = &ntsc_m_levels_svideo,
+ .svideo_color = &ntsc_m_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "NTSC-443",
+ .clock = 107520,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+ /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */
+ .hsync_end = 64, .hblank_end = 124,
+ .hblank_start = 836, .htotal = 857,
+
+ .progressive = false, .trilevel_sync = false,
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = true, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 20, .vi_end_f2 = 21,
+ .nbr_end = 240,
+
+ .burst_ena = 8,
+ .hburst_start = 72, .hburst_len = 34,
+ .vburst_start_f1 = 9, .vburst_end_f1 = 240,
+ .vburst_start_f2 = 10, .vburst_end_f2 = 240,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 240,
+ .vburst_start_f4 = 10, .vburst_end_f4 = 240,
+
+ /* desired 4.4336180 actual 4.4336180 clock 107.52 */
+ .dda1_inc = 168,
+ .dda2_inc = 18557, .dda2_size = 20625,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_8,
+ .pal_burst = true,
+
+ .composite_levels = &ntsc_m_levels_composite,
+ .composite_color = &ntsc_m_csc_composite,
+ .svideo_levels = &ntsc_m_levels_svideo,
+ .svideo_color = &ntsc_m_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "NTSC-J",
+ .clock = 107520,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+
+ /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
+ .hsync_end = 64, .hblank_end = 124,
+ .hblank_start = 836, .htotal = 857,
+
+ .progressive = false, .trilevel_sync = false,
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = true, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 20, .vi_end_f2 = 21,
+ .nbr_end = 240,
+
+ .burst_ena = true,
+ .hburst_start = 72, .hburst_len = 34,
+ .vburst_start_f1 = 9, .vburst_end_f1 = 240,
+ .vburst_start_f2 = 10, .vburst_end_f2 = 240,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 240,
+ .vburst_start_f4 = 10, .vburst_end_f4 = 240,
+
+ /* desired 3.5800000 actual 3.5800000 clock 107.52 */
+ .dda1_inc = 136,
+ .dda2_inc = 7624, .dda2_size = 20013,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_4,
+ .pal_burst = false,
+
+ .composite_levels = &ntsc_j_levels_composite,
+ .composite_color = &ntsc_j_csc_composite,
+ .svideo_levels = &ntsc_j_levels_svideo,
+ .svideo_color = &ntsc_j_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "PAL-M",
+ .clock = 107520,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+
+ /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
+ .hsync_end = 64, .hblank_end = 124,
+ .hblank_start = 836, .htotal = 857,
+
+ .progressive = false, .trilevel_sync = false,
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = true, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 20, .vi_end_f2 = 21,
+ .nbr_end = 240,
+
+ .burst_ena = true,
+ .hburst_start = 72, .hburst_len = 34,
+ .vburst_start_f1 = 9, .vburst_end_f1 = 240,
+ .vburst_start_f2 = 10, .vburst_end_f2 = 240,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 240,
+ .vburst_start_f4 = 10, .vburst_end_f4 = 240,
+
+ /* desired 3.5800000 actual 3.5800000 clock 107.52 */
+ .dda1_inc = 136,
+ .dda2_inc = 7624, .dda2_size = 20013,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_4,
+ .pal_burst = false,
+
+ .composite_levels = &pal_m_levels_composite,
+ .composite_color = &pal_m_csc_composite,
+ .svideo_levels = &pal_m_levels_svideo,
+ .svideo_color = &pal_m_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
+ .name = "PAL-N",
+ .clock = 107520,
+ .refresh = 25000,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+
+ .hsync_end = 64, .hblank_end = 128,
+ .hblank_start = 844, .htotal = 863,
+
+ .progressive = false, .trilevel_sync = false,
+
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = true, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 24, .vi_end_f2 = 25,
+ .nbr_end = 286,
+
+ .burst_ena = true,
+ .hburst_start = 73, .hburst_len = 34,
+ .vburst_start_f1 = 8, .vburst_end_f1 = 285,
+ .vburst_start_f2 = 8, .vburst_end_f2 = 286,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 286,
+ .vburst_start_f4 = 9, .vburst_end_f4 = 285,
+
+
+ /* desired 4.4336180 actual 4.4336180 clock 107.52 */
+ .dda1_inc = 168,
+ .dda2_inc = 18557, .dda2_size = 20625,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_8,
+ .pal_burst = true,
+
+ .composite_levels = &pal_n_levels_composite,
+ .composite_color = &pal_n_csc_composite,
+ .svideo_levels = &pal_n_levels_svideo,
+ .svideo_color = &pal_n_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
+ .name = "PAL",
+ .clock = 107520,
+ .refresh = 25000,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+
+ .hsync_end = 64, .hblank_end = 128,
+ .hblank_start = 844, .htotal = 863,
+
+ .progressive = false, .trilevel_sync = false,
+
+ .vsync_start_f1 = 5, .vsync_start_f2 = 6,
+ .vsync_len = 5,
+
+ .veq_ena = true, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 15,
+
+ .vi_end_f1 = 24, .vi_end_f2 = 25,
+ .nbr_end = 286,
+
+ .burst_ena = true,
+ .hburst_start = 73, .hburst_len = 32,
+ .vburst_start_f1 = 8, .vburst_end_f1 = 285,
+ .vburst_start_f2 = 8, .vburst_end_f2 = 286,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 286,
+ .vburst_start_f4 = 9, .vburst_end_f4 = 285,
+
+ /* desired 4.4336180 actual 4.4336180 clock 107.52 */
+ .dda1_inc = 168,
+ .dda2_inc = 18557, .dda2_size = 20625,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_8,
+ .pal_burst = true,
+
+ .composite_levels = &pal_levels_composite,
+ .composite_color = &pal_csc_composite,
+ .svideo_levels = &pal_levels_svideo,
+ .svideo_color = &pal_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "480p@59.94Hz",
+ .clock = 107520,
+ .refresh = 59940,
+ .oversample = TV_OVERSAMPLE_4X,
+ .component_only = 1,
+
+ .hsync_end = 64, .hblank_end = 122,
+ .hblank_start = 842, .htotal = 857,
+
+ .progressive = true,.trilevel_sync = false,
+
+ .vsync_start_f1 = 12, .vsync_start_f2 = 12,
+ .vsync_len = 12,
+
+ .veq_ena = false,
+
+ .vi_end_f1 = 44, .vi_end_f2 = 44,
+ .nbr_end = 496,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "480p@60Hz",
+ .clock = 107520,
+ .refresh = 60000,
+ .oversample = TV_OVERSAMPLE_4X,
+ .component_only = 1,
+
+ .hsync_end = 64, .hblank_end = 122,
+ .hblank_start = 842, .htotal = 856,
+
+ .progressive = true,.trilevel_sync = false,
+
+ .vsync_start_f1 = 12, .vsync_start_f2 = 12,
+ .vsync_len = 12,
+
+ .veq_ena = false,
+
+ .vi_end_f1 = 44, .vi_end_f2 = 44,
+ .nbr_end = 496,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "576p",
+ .clock = 107520,
+ .refresh = 50000,
+ .oversample = TV_OVERSAMPLE_4X,
+ .component_only = 1,
+
+ .hsync_end = 64, .hblank_end = 139,
+ .hblank_start = 859, .htotal = 863,
+
+ .progressive = true, .trilevel_sync = false,
+
+ .vsync_start_f1 = 10, .vsync_start_f2 = 10,
+ .vsync_len = 10,
+
+ .veq_ena = false,
+
+ .vi_end_f1 = 48, .vi_end_f2 = 48,
+ .nbr_end = 575,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "720p@60Hz",
+ .clock = 148800,
+ .refresh = 60000,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 80, .hblank_end = 300,
+ .hblank_start = 1580, .htotal = 1649,
+
+ .progressive = true, .trilevel_sync = true,
+
+ .vsync_start_f1 = 10, .vsync_start_f2 = 10,
+ .vsync_len = 10,
+
+ .veq_ena = false,
+
+ .vi_end_f1 = 29, .vi_end_f2 = 29,
+ .nbr_end = 719,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "720p@59.94Hz",
+ .clock = 148800,
+ .refresh = 59940,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 80, .hblank_end = 300,
+ .hblank_start = 1580, .htotal = 1651,
+
+ .progressive = true, .trilevel_sync = true,
+
+ .vsync_start_f1 = 10, .vsync_start_f2 = 10,
+ .vsync_len = 10,
+
+ .veq_ena = false,
+
+ .vi_end_f1 = 29, .vi_end_f2 = 29,
+ .nbr_end = 719,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "720p@50Hz",
+ .clock = 148800,
+ .refresh = 50000,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 80, .hblank_end = 300,
+ .hblank_start = 1580, .htotal = 1979,
+
+ .progressive = true, .trilevel_sync = true,
+
+ .vsync_start_f1 = 10, .vsync_start_f2 = 10,
+ .vsync_len = 10,
+
+ .veq_ena = false,
+
+ .vi_end_f1 = 29, .vi_end_f2 = 29,
+ .nbr_end = 719,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ .max_srcw = 800
+ },
+ {
+ .name = "1080i@50Hz",
+ .clock = 148800,
+ .refresh = 25000,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 88, .hblank_end = 235,
+ .hblank_start = 2155, .htotal = 2639,
+
+ .progressive = false, .trilevel_sync = true,
+
+ .vsync_start_f1 = 4, .vsync_start_f2 = 5,
+ .vsync_len = 10,
+
+ .veq_ena = true, .veq_start_f1 = 4,
+ .veq_start_f2 = 4, .veq_len = 10,
+
+
+ .vi_end_f1 = 21, .vi_end_f2 = 22,
+ .nbr_end = 539,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "1080i@60Hz",
+ .clock = 148800,
+ .refresh = 30000,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 88, .hblank_end = 235,
+ .hblank_start = 2155, .htotal = 2199,
+
+ .progressive = false, .trilevel_sync = true,
+
+ .vsync_start_f1 = 4, .vsync_start_f2 = 5,
+ .vsync_len = 10,
+
+ .veq_ena = true, .veq_start_f1 = 4,
+ .veq_start_f2 = 4, .veq_len = 10,
+
+
+ .vi_end_f1 = 21, .vi_end_f2 = 22,
+ .nbr_end = 539,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "1080i@59.94Hz",
+ .clock = 148800,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 88, .hblank_end = 235,
+ .hblank_start = 2155, .htotal = 2200,
+
+ .progressive = false, .trilevel_sync = true,
+
+ .vsync_start_f1 = 4, .vsync_start_f2 = 5,
+ .vsync_len = 10,
+
+ .veq_ena = true, .veq_start_f1 = 4,
+ .veq_start_f2 = 4, .veq_len = 10,
+
+
+ .vi_end_f1 = 21, .vi_end_f2 = 22,
+ .nbr_end = 539,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+};
+
+#define NUM_TV_MODES sizeof(tv_modes) / sizeof (tv_modes[0])
+
+static void
+intel_tv_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ switch(mode) {
+ case DRM_MODE_DPMS_ON:
+ I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
+ break;
+ }
+}
+
+static void
+intel_tv_save(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ int i;
+
+ tv_priv->save_TV_H_CTL_1 = I915_READ(TV_H_CTL_1);
+ tv_priv->save_TV_H_CTL_2 = I915_READ(TV_H_CTL_2);
+ tv_priv->save_TV_H_CTL_3 = I915_READ(TV_H_CTL_3);
+ tv_priv->save_TV_V_CTL_1 = I915_READ(TV_V_CTL_1);
+ tv_priv->save_TV_V_CTL_2 = I915_READ(TV_V_CTL_2);
+ tv_priv->save_TV_V_CTL_3 = I915_READ(TV_V_CTL_3);
+ tv_priv->save_TV_V_CTL_4 = I915_READ(TV_V_CTL_4);
+ tv_priv->save_TV_V_CTL_5 = I915_READ(TV_V_CTL_5);
+ tv_priv->save_TV_V_CTL_6 = I915_READ(TV_V_CTL_6);
+ tv_priv->save_TV_V_CTL_7 = I915_READ(TV_V_CTL_7);
+ tv_priv->save_TV_SC_CTL_1 = I915_READ(TV_SC_CTL_1);
+ tv_priv->save_TV_SC_CTL_2 = I915_READ(TV_SC_CTL_2);
+ tv_priv->save_TV_SC_CTL_3 = I915_READ(TV_SC_CTL_3);
+
+ tv_priv->save_TV_CSC_Y = I915_READ(TV_CSC_Y);
+ tv_priv->save_TV_CSC_Y2 = I915_READ(TV_CSC_Y2);
+ tv_priv->save_TV_CSC_U = I915_READ(TV_CSC_U);
+ tv_priv->save_TV_CSC_U2 = I915_READ(TV_CSC_U2);
+ tv_priv->save_TV_CSC_V = I915_READ(TV_CSC_V);
+ tv_priv->save_TV_CSC_V2 = I915_READ(TV_CSC_V2);
+ tv_priv->save_TV_CLR_KNOBS = I915_READ(TV_CLR_KNOBS);
+ tv_priv->save_TV_CLR_LEVEL = I915_READ(TV_CLR_LEVEL);
+ tv_priv->save_TV_WIN_POS = I915_READ(TV_WIN_POS);
+ tv_priv->save_TV_WIN_SIZE = I915_READ(TV_WIN_SIZE);
+ tv_priv->save_TV_FILTER_CTL_1 = I915_READ(TV_FILTER_CTL_1);
+ tv_priv->save_TV_FILTER_CTL_2 = I915_READ(TV_FILTER_CTL_2);
+ tv_priv->save_TV_FILTER_CTL_3 = I915_READ(TV_FILTER_CTL_3);
+
+ for (i = 0; i < 60; i++)
+ tv_priv->save_TV_H_LUMA[i] = I915_READ(TV_H_LUMA_0 + (i <<2));
+ for (i = 0; i < 60; i++)
+ tv_priv->save_TV_H_CHROMA[i] = I915_READ(TV_H_CHROMA_0 + (i <<2));
+ for (i = 0; i < 43; i++)
+ tv_priv->save_TV_V_LUMA[i] = I915_READ(TV_V_LUMA_0 + (i <<2));
+ for (i = 0; i < 43; i++)
+ tv_priv->save_TV_V_CHROMA[i] = I915_READ(TV_V_CHROMA_0 + (i <<2));
+
+ tv_priv->save_TV_DAC = I915_READ(TV_DAC);
+ tv_priv->save_TV_CTL = I915_READ(TV_CTL);
+}
+
+static void
+intel_tv_restore(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ struct drm_crtc *crtc = connector->encoder->crtc;
+ struct intel_crtc *intel_crtc;
+ int i;
+
+ /* FIXME: No CRTC? */
+ if (!crtc)
+ return;
+
+ intel_crtc = to_intel_crtc(crtc);
+ I915_WRITE(TV_H_CTL_1, tv_priv->save_TV_H_CTL_1);
+ I915_WRITE(TV_H_CTL_2, tv_priv->save_TV_H_CTL_2);
+ I915_WRITE(TV_H_CTL_3, tv_priv->save_TV_H_CTL_3);
+ I915_WRITE(TV_V_CTL_1, tv_priv->save_TV_V_CTL_1);
+ I915_WRITE(TV_V_CTL_2, tv_priv->save_TV_V_CTL_2);
+ I915_WRITE(TV_V_CTL_3, tv_priv->save_TV_V_CTL_3);
+ I915_WRITE(TV_V_CTL_4, tv_priv->save_TV_V_CTL_4);
+ I915_WRITE(TV_V_CTL_5, tv_priv->save_TV_V_CTL_5);
+ I915_WRITE(TV_V_CTL_6, tv_priv->save_TV_V_CTL_6);
+ I915_WRITE(TV_V_CTL_7, tv_priv->save_TV_V_CTL_7);
+ I915_WRITE(TV_SC_CTL_1, tv_priv->save_TV_SC_CTL_1);
+ I915_WRITE(TV_SC_CTL_2, tv_priv->save_TV_SC_CTL_2);
+ I915_WRITE(TV_SC_CTL_3, tv_priv->save_TV_SC_CTL_3);
+
+ I915_WRITE(TV_CSC_Y, tv_priv->save_TV_CSC_Y);
+ I915_WRITE(TV_CSC_Y2, tv_priv->save_TV_CSC_Y2);
+ I915_WRITE(TV_CSC_U, tv_priv->save_TV_CSC_U);
+ I915_WRITE(TV_CSC_U2, tv_priv->save_TV_CSC_U2);
+ I915_WRITE(TV_CSC_V, tv_priv->save_TV_CSC_V);
+ I915_WRITE(TV_CSC_V2, tv_priv->save_TV_CSC_V2);
+ I915_WRITE(TV_CLR_KNOBS, tv_priv->save_TV_CLR_KNOBS);
+ I915_WRITE(TV_CLR_LEVEL, tv_priv->save_TV_CLR_LEVEL);
+
+ {
+ int pipeconf_reg = (intel_crtc->pipe == 0) ?
+ PIPEACONF : PIPEBCONF;
+ int dspcntr_reg = (intel_crtc->plane == 0) ?
+ DSPACNTR : DSPBCNTR;
+ int pipeconf = I915_READ(pipeconf_reg);
+ int dspcntr = I915_READ(dspcntr_reg);
+ int dspbase_reg = (intel_crtc->plane == 0) ?
+ DSPAADDR : DSPBADDR;
+ /* Pipe must be off here */
+ I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+
+ if (!IS_I9XX(dev)) {
+ /* Wait for vblank for the disable to take effect */
+ intel_wait_for_vblank(dev);
+ }
+
+ I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE);
+ /* Wait for vblank for the disable to take effect. */
+ intel_wait_for_vblank(dev);
+
+ /* Filter ctl must be set before TV_WIN_SIZE */
+ I915_WRITE(TV_FILTER_CTL_1, tv_priv->save_TV_FILTER_CTL_1);
+ I915_WRITE(TV_FILTER_CTL_2, tv_priv->save_TV_FILTER_CTL_2);
+ I915_WRITE(TV_FILTER_CTL_3, tv_priv->save_TV_FILTER_CTL_3);
+ I915_WRITE(TV_WIN_POS, tv_priv->save_TV_WIN_POS);
+ I915_WRITE(TV_WIN_SIZE, tv_priv->save_TV_WIN_SIZE);
+ I915_WRITE(pipeconf_reg, pipeconf);
+ I915_WRITE(dspcntr_reg, dspcntr);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ }
+
+ for (i = 0; i < 60; i++)
+ I915_WRITE(TV_H_LUMA_0 + (i <<2), tv_priv->save_TV_H_LUMA[i]);
+ for (i = 0; i < 60; i++)
+ I915_WRITE(TV_H_CHROMA_0 + (i <<2), tv_priv->save_TV_H_CHROMA[i]);
+ for (i = 0; i < 43; i++)
+ I915_WRITE(TV_V_LUMA_0 + (i <<2), tv_priv->save_TV_V_LUMA[i]);
+ for (i = 0; i < 43; i++)
+ I915_WRITE(TV_V_CHROMA_0 + (i <<2), tv_priv->save_TV_V_CHROMA[i]);
+
+ I915_WRITE(TV_DAC, tv_priv->save_TV_DAC);
+ I915_WRITE(TV_CTL, tv_priv->save_TV_CTL);
+}
+
+static const struct tv_mode *
+intel_tv_mode_lookup (char *tv_format)
+{
+ int i;
+
+ for (i = 0; i < sizeof(tv_modes) / sizeof (tv_modes[0]); i++) {
+ const struct tv_mode *tv_mode = &tv_modes[i];
+
+ if (!strcmp(tv_format, tv_mode->name))
+ return tv_mode;
+ }
+ return NULL;
+}
+
+static const struct tv_mode *
+intel_tv_mode_find (struct intel_output *intel_output)
+{
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+
+ return intel_tv_mode_lookup(tv_priv->tv_format);
+}
+
+static enum drm_mode_status
+intel_tv_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
+
+ /* Ensure TV refresh is close to desired refresh */
+ if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode)) < 1)
+ return MODE_OK;
+ return MODE_CLOCK_RANGE;
+}
+
+
+static bool
+intel_tv_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_mode_config *drm_config = &dev->mode_config;
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ const struct tv_mode *tv_mode = intel_tv_mode_find (intel_output);
+ struct drm_encoder *other_encoder;
+
+ if (!tv_mode)
+ return false;
+
+ /* FIXME: lock encoder list */
+ list_for_each_entry(other_encoder, &drm_config->encoder_list, head) {
+ if (other_encoder != encoder &&
+ other_encoder->crtc == encoder->crtc)
+ return false;
+ }
+
+ adjusted_mode->clock = tv_mode->clock;
+ return true;
+}
+
+static void
+intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
+ u32 tv_ctl;
+ u32 hctl1, hctl2, hctl3;
+ u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
+ u32 scctl1, scctl2, scctl3;
+ int i, j;
+ const struct video_levels *video_levels;
+ const struct color_conversion *color_conversion;
+ bool burst_ena;
+
+ if (!tv_mode)
+ return; /* can't happen (mode_prepare prevents this) */
+
+ tv_ctl = 0;
+
+ switch (tv_priv->type) {
+ default:
+ case DRM_MODE_CONNECTOR_Unknown:
+ case DRM_MODE_CONNECTOR_Composite:
+ tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
+ video_levels = tv_mode->composite_levels;
+ color_conversion = tv_mode->composite_color;
+ burst_ena = tv_mode->burst_ena;
+ break;
+ case DRM_MODE_CONNECTOR_Component:
+ tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
+ video_levels = &component_levels;
+ if (tv_mode->burst_ena)
+ color_conversion = &sdtv_csc_yprpb;
+ else
+ color_conversion = &hdtv_csc_yprpb;
+ burst_ena = false;
+ break;
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
+ video_levels = tv_mode->svideo_levels;
+ color_conversion = tv_mode->svideo_color;
+ burst_ena = tv_mode->burst_ena;
+ break;
+ }
+ hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
+ (tv_mode->htotal << TV_HTOTAL_SHIFT);
+
+ hctl2 = (tv_mode->hburst_start << 16) |
+ (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
+
+ if (burst_ena)
+ hctl2 |= TV_BURST_ENA;
+
+ hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
+ (tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
+
+ vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
+ (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
+ (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
+
+ vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
+ (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
+ (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
+
+ vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
+ (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
+ (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
+
+ if (tv_mode->veq_ena)
+ vctl3 |= TV_EQUAL_ENA;
+
+ vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
+ (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
+
+ vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
+ (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
+
+ vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
+ (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
+
+ vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
+ (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
+
+ if (intel_crtc->pipe == 1)
+ tv_ctl |= TV_ENC_PIPEB_SELECT;
+ tv_ctl |= tv_mode->oversample;
+
+ if (tv_mode->progressive)
+ tv_ctl |= TV_PROGRESSIVE;
+ if (tv_mode->trilevel_sync)
+ tv_ctl |= TV_TRILEVEL_SYNC;
+ if (tv_mode->pal_burst)
+ tv_ctl |= TV_PAL_BURST;
+ scctl1 = 0;
+ /* dda1 implies valid video levels */
+ if (tv_mode->dda1_inc) {
+ scctl1 |= TV_SC_DDA1_EN;
+ scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
+ }
+
+ if (tv_mode->dda2_inc)
+ scctl1 |= TV_SC_DDA2_EN;
+
+ if (tv_mode->dda3_inc)
+ scctl1 |= TV_SC_DDA3_EN;
+
+ scctl1 |= tv_mode->sc_reset;
+ scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
+
+ scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
+ tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
+
+ scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
+ tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
+
+ /* Enable two fixes for the chips that need them. */
+ if (dev->pci_device < 0x2772)
+ tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
+
+ I915_WRITE(TV_H_CTL_1, hctl1);
+ I915_WRITE(TV_H_CTL_2, hctl2);
+ I915_WRITE(TV_H_CTL_3, hctl3);
+ I915_WRITE(TV_V_CTL_1, vctl1);
+ I915_WRITE(TV_V_CTL_2, vctl2);
+ I915_WRITE(TV_V_CTL_3, vctl3);
+ I915_WRITE(TV_V_CTL_4, vctl4);
+ I915_WRITE(TV_V_CTL_5, vctl5);
+ I915_WRITE(TV_V_CTL_6, vctl6);
+ I915_WRITE(TV_V_CTL_7, vctl7);
+ I915_WRITE(TV_SC_CTL_1, scctl1);
+ I915_WRITE(TV_SC_CTL_2, scctl2);
+ I915_WRITE(TV_SC_CTL_3, scctl3);
+
+ if (color_conversion) {
+ I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
+ color_conversion->gy);
+ I915_WRITE(TV_CSC_Y2,(color_conversion->by << 16) |
+ color_conversion->ay);
+ I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
+ color_conversion->gu);
+ I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
+ color_conversion->au);
+ I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
+ color_conversion->gv);
+ I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
+ color_conversion->av);
+ }
+
+ I915_WRITE(TV_CLR_KNOBS, 0x00606000);
+ if (video_levels)
+ I915_WRITE(TV_CLR_LEVEL,
+ ((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
+ (video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
+ {
+ int pipeconf_reg = (intel_crtc->pipe == 0) ?
+ PIPEACONF : PIPEBCONF;
+ int dspcntr_reg = (intel_crtc->plane == 0) ?
+ DSPACNTR : DSPBCNTR;
+ int pipeconf = I915_READ(pipeconf_reg);
+ int dspcntr = I915_READ(dspcntr_reg);
+ int dspbase_reg = (intel_crtc->plane == 0) ?
+ DSPAADDR : DSPBADDR;
+ int xpos = 0x0, ypos = 0x0;
+ unsigned int xsize, ysize;
+ /* Pipe must be off here */
+ I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+
+ /* Wait for vblank for the disable to take effect */
+ if (!IS_I9XX(dev))
+ intel_wait_for_vblank(dev);
+
+ I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE);
+ /* Wait for vblank for the disable to take effect. */
+ intel_wait_for_vblank(dev);
+
+ /* Filter ctl must be set before TV_WIN_SIZE */
+ I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
+ xsize = tv_mode->hblank_start - tv_mode->hblank_end;
+ if (tv_mode->progressive)
+ ysize = tv_mode->nbr_end + 1;
+ else
+ ysize = 2*tv_mode->nbr_end + 1;
+
+ xpos += tv_priv->margin[TV_MARGIN_LEFT];
+ ypos += tv_priv->margin[TV_MARGIN_TOP];
+ xsize -= (tv_priv->margin[TV_MARGIN_LEFT] +
+ tv_priv->margin[TV_MARGIN_RIGHT]);
+ ysize -= (tv_priv->margin[TV_MARGIN_TOP] +
+ tv_priv->margin[TV_MARGIN_BOTTOM]);
+ I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
+ I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
+
+ I915_WRITE(pipeconf_reg, pipeconf);
+ I915_WRITE(dspcntr_reg, dspcntr);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ }
+
+ j = 0;
+ for (i = 0; i < 60; i++)
+ I915_WRITE(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
+ for (i = 0; i < 60; i++)
+ I915_WRITE(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
+ for (i = 0; i < 43; i++)
+ I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
+ for (i = 0; i < 43; i++)
+ I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
+ I915_WRITE(TV_DAC, 0);
+ I915_WRITE(TV_CTL, tv_ctl);
+}
+
+static const struct drm_display_mode reported_modes[] = {
+ {
+ .name = "NTSC 480i",
+ .clock = 107520,
+ .hdisplay = 1280,
+ .hsync_start = 1368,
+ .hsync_end = 1496,
+ .htotal = 1712,
+
+ .vdisplay = 1024,
+ .vsync_start = 1027,
+ .vsync_end = 1034,
+ .vtotal = 1104,
+ .type = DRM_MODE_TYPE_DRIVER,
+ },
+};
+
+/**
+ * Detects TV presence by checking for load.
+ *
+ * Requires that the current pipe's DPLL is active.
+
+ * \return true if TV is connected.
+ * \return false if TV is disconnected.
+ */
+static int
+intel_tv_detect_type (struct drm_crtc *crtc, struct intel_output *intel_output)
+{
+ struct drm_encoder *encoder = &intel_output->enc;
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned long irqflags;
+ u32 tv_ctl, save_tv_ctl;
+ u32 tv_dac, save_tv_dac;
+ int type = DRM_MODE_CONNECTOR_Unknown;
+
+ tv_dac = I915_READ(TV_DAC);
+
+ /* Disable TV interrupts around load detect or we'll recurse */
+ spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
+ i915_disable_pipestat(dev_priv, 0, PIPE_HOTPLUG_INTERRUPT_ENABLE |
+ PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
+ spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
+
+ /*
+ * Detect TV by polling)
+ */
+ if (intel_output->load_detect_temp) {
+ /* TV not currently running, prod it with destructive detect */
+ save_tv_dac = tv_dac;
+ tv_ctl = I915_READ(TV_CTL);
+ save_tv_ctl = tv_ctl;
+ tv_ctl &= ~TV_ENC_ENABLE;
+ tv_ctl &= ~TV_TEST_MODE_MASK;
+ tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
+ tv_dac &= ~TVDAC_SENSE_MASK;
+ tv_dac |= (TVDAC_STATE_CHG_EN |
+ TVDAC_A_SENSE_CTL |
+ TVDAC_B_SENSE_CTL |
+ TVDAC_C_SENSE_CTL |
+ DAC_CTL_OVERRIDE |
+ DAC_A_0_7_V |
+ DAC_B_0_7_V |
+ DAC_C_0_7_V);
+ I915_WRITE(TV_CTL, tv_ctl);
+ I915_WRITE(TV_DAC, tv_dac);
+ intel_wait_for_vblank(dev);
+ tv_dac = I915_READ(TV_DAC);
+ I915_WRITE(TV_DAC, save_tv_dac);
+ I915_WRITE(TV_CTL, save_tv_ctl);
+ }
+ /*
+ * A B C
+ * 0 1 1 Composite
+ * 1 0 X svideo
+ * 0 0 0 Component
+ */
+ if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
+ DRM_DEBUG("Detected Composite TV connection\n");
+ type = DRM_MODE_CONNECTOR_Composite;
+ } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
+ DRM_DEBUG("Detected S-Video TV connection\n");
+ type = DRM_MODE_CONNECTOR_SVIDEO;
+ } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
+ DRM_DEBUG("Detected Component TV connection\n");
+ type = DRM_MODE_CONNECTOR_Component;
+ } else {
+ DRM_DEBUG("No TV connection detected\n");
+ type = -1;
+ }
+
+ /* Restore interrupt config */
+ spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
+ i915_enable_pipestat(dev_priv, 0, PIPE_HOTPLUG_INTERRUPT_ENABLE |
+ PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
+ spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
+
+ return type;
+}
+
+/**
+ * Detect the TV connection.
+ *
+ * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure
+ * we have a pipe programmed in order to probe the TV.
+ */
+static enum drm_connector_status
+intel_tv_detect(struct drm_connector *connector)
+{
+ struct drm_crtc *crtc;
+ struct drm_display_mode mode;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ struct drm_encoder *encoder = &intel_output->enc;
+ int dpms_mode;
+ int type = tv_priv->type;
+
+ mode = reported_modes[0];
+ drm_mode_set_crtcinfo(&mode, CRTC_INTERLACE_HALVE_V);
+
+ if (encoder->crtc) {
+ type = intel_tv_detect_type(encoder->crtc, intel_output);
+ } else {
+ crtc = intel_get_load_detect_pipe(intel_output, &mode, &dpms_mode);
+ if (crtc) {
+ type = intel_tv_detect_type(crtc, intel_output);
+ intel_release_load_detect_pipe(intel_output, dpms_mode);
+ } else
+ type = -1;
+ }
+
+ if (type < 0)
+ return connector_status_disconnected;
+
+ return connector_status_connected;
+}
+
+static struct input_res {
+ char *name;
+ int w, h;
+} input_res_table[] =
+{
+ {"640x480", 640, 480},
+ {"800x600", 800, 600},
+ {"1024x768", 1024, 768},
+ {"1280x1024", 1280, 1024},
+ {"848x480", 848, 480},
+ {"1280x720", 1280, 720},
+ {"1920x1080", 1920, 1080},
+};
+
+/**
+ * Stub get_modes function.
+ *
+ * This should probably return a set of fixed modes, unless we can figure out
+ * how to probe modes off of TV connections.
+ */
+
+static int
+intel_tv_get_modes(struct drm_connector *connector)
+{
+ struct drm_display_mode *mode_ptr;
+ struct intel_output *intel_output = to_intel_output(connector);
+ const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
+ int j;
+
+ for (j = 0; j < sizeof(input_res_table) / sizeof(input_res_table[0]);
+ j++) {
+ struct input_res *input = &input_res_table[j];
+ unsigned int hactive_s = input->w;
+ unsigned int vactive_s = input->h;
+
+ if (tv_mode->max_srcw && input->w > tv_mode->max_srcw)
+ continue;
+
+ if (input->w > 1024 && (!tv_mode->progressive
+ && !tv_mode->component_only))
+ continue;
+
+ mode_ptr = drm_calloc(1, sizeof(struct drm_display_mode),
+ DRM_MEM_DRIVER);
+ strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN);
+
+ mode_ptr->hdisplay = hactive_s;
+ mode_ptr->hsync_start = hactive_s + 1;
+ mode_ptr->hsync_end = hactive_s + 64;
+ if (mode_ptr->hsync_end <= mode_ptr->hsync_start)
+ mode_ptr->hsync_end = mode_ptr->hsync_start + 1;
+ mode_ptr->htotal = hactive_s + 96;
+
+ mode_ptr->vdisplay = vactive_s;
+ mode_ptr->vsync_start = vactive_s + 1;
+ mode_ptr->vsync_end = vactive_s + 32;
+ if (mode_ptr->vsync_end <= mode_ptr->vsync_start)
+ mode_ptr->vsync_end = mode_ptr->vsync_start + 1;
+ mode_ptr->vtotal = vactive_s + 33;
+
+ mode_ptr->clock = (int) (tv_mode->refresh *
+ mode_ptr->vtotal *
+ mode_ptr->htotal / 1000) / 1000;
+
+ mode_ptr->type = DRM_MODE_TYPE_DRIVER;
+ drm_mode_probed_add(connector, mode_ptr);
+ }
+
+ return 0;
+}
+
+static void
+intel_tv_destroy (struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ drm_free(intel_output, sizeof(struct intel_output) + sizeof(struct intel_tv_priv),
+ DRM_MEM_DRIVER);
+}
+
+
+static int
+intel_tv_set_property(struct drm_connector *connector, struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_device *dev = connector->dev;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ int ret = 0;
+
+ ret = drm_connector_property_set_value(connector, property, val);
+ if (ret < 0)
+ goto out;
+
+ if (property == dev->mode_config.tv_left_margin_property)
+ tv_priv->margin[TV_MARGIN_LEFT] = val;
+ else if (property == dev->mode_config.tv_right_margin_property)
+ tv_priv->margin[TV_MARGIN_RIGHT] = val;
+ else if (property == dev->mode_config.tv_top_margin_property)
+ tv_priv->margin[TV_MARGIN_TOP] = val;
+ else if (property == dev->mode_config.tv_bottom_margin_property)
+ tv_priv->margin[TV_MARGIN_BOTTOM] = val;
+ else if (property == dev->mode_config.tv_mode_property) {
+ if (val >= NUM_TV_MODES) {
+ ret = -EINVAL;
+ goto out;
+ }
+ tv_priv->tv_format = tv_modes[val].name;
+ intel_tv_mode_set(&intel_output->enc, NULL, NULL);
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ intel_tv_mode_set(&intel_output->enc, NULL, NULL);
+out:
+ return ret;
+}
+
+static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
+ .dpms = intel_tv_dpms,
+ .mode_fixup = intel_tv_mode_fixup,
+ .prepare = intel_encoder_prepare,
+ .mode_set = intel_tv_mode_set,
+ .commit = intel_encoder_commit,
+};
+
+static const struct drm_connector_funcs intel_tv_connector_funcs = {
+ .save = intel_tv_save,
+ .restore = intel_tv_restore,
+ .detect = intel_tv_detect,
+ .destroy = intel_tv_destroy,
+ .set_property = intel_tv_set_property,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+};
+
+static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
+ .mode_valid = intel_tv_mode_valid,
+ .get_modes = intel_tv_get_modes,
+ .best_encoder = intel_best_encoder,
+};
+
+static void intel_tv_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_tv_enc_funcs = {
+ .destroy = intel_tv_enc_destroy,
+};
+
+
+void
+intel_tv_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_connector *connector;
+ struct intel_output *intel_output;
+ struct intel_tv_priv *tv_priv;
+ u32 tv_dac_on, tv_dac_off, save_tv_dac;
+ char **tv_format_names;
+ int i, initial_mode = 0;
+
+ if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
+ return;
+
+ /* Even if we have an encoder we may not have a connector */
+ if (!dev_priv->int_tv_support)
+ return;
+
+ /*
+ * Sanity check the TV output by checking to see if the
+ * DAC register holds a value
+ */
+ save_tv_dac = I915_READ(TV_DAC);
+
+ I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN);
+ tv_dac_on = I915_READ(TV_DAC);
+
+ I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
+ tv_dac_off = I915_READ(TV_DAC);
+
+ I915_WRITE(TV_DAC, save_tv_dac);
+
+ /*
+ * If the register does not hold the state change enable
+ * bit, (either as a 0 or a 1), assume it doesn't really
+ * exist
+ */
+ if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
+ (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
+ return;
+
+ intel_output = drm_calloc(1, sizeof(struct intel_output) +
+ sizeof(struct intel_tv_priv), DRM_MEM_DRIVER);
+ if (!intel_output) {
+ return;
+ }
+ connector = &intel_output->base;
+
+ drm_connector_init(dev, connector, &intel_tv_connector_funcs,
+ DRM_MODE_CONNECTOR_SVIDEO);
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_tv_enc_funcs,
+ DRM_MODE_ENCODER_TVDAC);
+
+ drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
+ tv_priv = (struct intel_tv_priv *)(intel_output + 1);
+ intel_output->type = INTEL_OUTPUT_TVOUT;
+ intel_output->enc.possible_crtcs = ((1 << 0) | (1 << 1));
+ intel_output->enc.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
+ intel_output->dev_priv = tv_priv;
+ tv_priv->type = DRM_MODE_CONNECTOR_Unknown;
+
+ /* BIOS margin values */
+ tv_priv->margin[TV_MARGIN_LEFT] = 54;
+ tv_priv->margin[TV_MARGIN_TOP] = 36;
+ tv_priv->margin[TV_MARGIN_RIGHT] = 46;
+ tv_priv->margin[TV_MARGIN_BOTTOM] = 37;
+
+ tv_priv->tv_format = kstrdup(tv_modes[initial_mode].name, GFP_KERNEL);
+
+ drm_encoder_helper_add(&intel_output->enc, &intel_tv_helper_funcs);
+ drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+ /* Create TV properties then attach current values */
+ tv_format_names = drm_alloc(sizeof(char *) * NUM_TV_MODES,
+ DRM_MEM_DRIVER);
+ if (!tv_format_names)
+ goto out;
+ for (i = 0; i < NUM_TV_MODES; i++)
+ tv_format_names[i] = tv_modes[i].name;
+ drm_mode_create_tv_properties(dev, NUM_TV_MODES, tv_format_names);
+
+ drm_connector_attach_property(connector, dev->mode_config.tv_mode_property,
+ initial_mode);
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_left_margin_property,
+ tv_priv->margin[TV_MARGIN_LEFT]);
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_top_margin_property,
+ tv_priv->margin[TV_MARGIN_TOP]);
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_right_margin_property,
+ tv_priv->margin[TV_MARGIN_RIGHT]);
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_bottom_margin_property,
+ tv_priv->margin[TV_MARGIN_BOTTOM]);
+out:
+ drm_sysfs_connector_add(connector);
+}
diff --git a/drivers/gpu/drm/radeon/r300_cmdbuf.c b/drivers/gpu/drm/radeon/r300_cmdbuf.c
index 4b27d9abb7bc..cace3964feeb 100644
--- a/drivers/gpu/drm/radeon/r300_cmdbuf.c
+++ b/drivers/gpu/drm/radeon/r300_cmdbuf.c
@@ -860,12 +860,12 @@ static __inline__ void r300_pacify(drm_radeon_private_t *dev_priv)
* The actual age emit is done by r300_do_cp_cmdbuf, which is why you must
* be careful about how this function is called.
*/
-static void r300_discard_buffer(struct drm_device * dev, struct drm_buf * buf)
+static void r300_discard_buffer(struct drm_device *dev, struct drm_master *master, struct drm_buf *buf)
{
- drm_radeon_private_t *dev_priv = dev->dev_private;
drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
- buf_priv->age = ++dev_priv->sarea_priv->last_dispatch;
+ buf_priv->age = ++master_priv->sarea_priv->last_dispatch;
buf->pending = 1;
buf->used = 0;
}
@@ -1027,6 +1027,7 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
drm_radeon_kcmd_buffer_t *cmdbuf)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
struct drm_device_dma *dma = dev->dma;
struct drm_buf *buf = NULL;
int emit_dispatch_age = 0;
@@ -1134,7 +1135,7 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
}
emit_dispatch_age = 1;
- r300_discard_buffer(dev, buf);
+ r300_discard_buffer(dev, file_priv->master, buf);
break;
case R300_CMD_WAIT:
@@ -1189,7 +1190,7 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
/* Emit the vertex buffer age */
BEGIN_RING(2);
- RADEON_DISPATCH_AGE(dev_priv->sarea_priv->last_dispatch);
+ RADEON_DISPATCH_AGE(master_priv->sarea_priv->last_dispatch);
ADVANCE_RING();
}
diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c
index dcebb4bee7aa..63212d7bbc28 100644
--- a/drivers/gpu/drm/radeon/radeon_cp.c
+++ b/drivers/gpu/drm/radeon/radeon_cp.c
@@ -31,6 +31,7 @@
#include "drmP.h"
#include "drm.h"
+#include "drm_sarea.h"
#include "radeon_drm.h"
#include "radeon_drv.h"
#include "r300_reg.h"
@@ -667,15 +668,14 @@ static void radeon_cp_init_ring_buffer(struct drm_device * dev,
RADEON_WRITE(RADEON_BUS_CNTL, tmp);
} /* PCIE cards appears to not need this */
- dev_priv->sarea_priv->last_frame = dev_priv->scratch[0] = 0;
- RADEON_WRITE(RADEON_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame);
+ dev_priv->scratch[0] = 0;
+ RADEON_WRITE(RADEON_LAST_FRAME_REG, 0);
- dev_priv->sarea_priv->last_dispatch = dev_priv->scratch[1] = 0;
- RADEON_WRITE(RADEON_LAST_DISPATCH_REG,
- dev_priv->sarea_priv->last_dispatch);
+ dev_priv->scratch[1] = 0;
+ RADEON_WRITE(RADEON_LAST_DISPATCH_REG, 0);
- dev_priv->sarea_priv->last_clear = dev_priv->scratch[2] = 0;
- RADEON_WRITE(RADEON_LAST_CLEAR_REG, dev_priv->sarea_priv->last_clear);
+ dev_priv->scratch[2] = 0;
+ RADEON_WRITE(RADEON_LAST_CLEAR_REG, 0);
radeon_do_wait_for_idle(dev_priv);
@@ -871,9 +871,11 @@ static void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on)
}
}
-static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init)
+static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
+ struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
DRM_DEBUG("\n");
@@ -998,8 +1000,8 @@ static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init)
dev_priv->buffers_offset = init->buffers_offset;
dev_priv->gart_textures_offset = init->gart_textures_offset;
- dev_priv->sarea = drm_getsarea(dev);
- if (!dev_priv->sarea) {
+ master_priv->sarea = drm_getsarea(dev);
+ if (!master_priv->sarea) {
DRM_ERROR("could not find sarea!\n");
radeon_do_cleanup_cp(dev);
return -EINVAL;
@@ -1035,10 +1037,6 @@ static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init)
}
}
- dev_priv->sarea_priv =
- (drm_radeon_sarea_t *) ((u8 *) dev_priv->sarea->handle +
- init->sarea_priv_offset);
-
#if __OS_HAS_AGP
if (dev_priv->flags & RADEON_IS_AGP) {
drm_core_ioremap(dev_priv->cp_ring, dev);
@@ -1329,7 +1327,7 @@ int radeon_cp_init(struct drm_device *dev, void *data, struct drm_file *file_pri
case RADEON_INIT_CP:
case RADEON_INIT_R200_CP:
case RADEON_INIT_R300_CP:
- return radeon_do_init_cp(dev, init);
+ return radeon_do_init_cp(dev, init, file_priv);
case RADEON_CLEANUP_CP:
return radeon_do_cleanup_cp(dev);
}
@@ -1768,6 +1766,51 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags)
return ret;
}
+int radeon_master_create(struct drm_device *dev, struct drm_master *master)
+{
+ struct drm_radeon_master_private *master_priv;
+ unsigned long sareapage;
+ int ret;
+
+ master_priv = drm_calloc(1, sizeof(*master_priv), DRM_MEM_DRIVER);
+ if (!master_priv)
+ return -ENOMEM;
+
+ /* prebuild the SAREA */
+ sareapage = max_t(unsigned long, SAREA_MAX, PAGE_SIZE);
+ ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK|_DRM_DRIVER,
+ &master_priv->sarea);
+ if (ret) {
+ DRM_ERROR("SAREA setup failed\n");
+ return ret;
+ }
+ master_priv->sarea_priv = master_priv->sarea->handle + sizeof(struct drm_sarea);
+ master_priv->sarea_priv->pfCurrentPage = 0;
+
+ master->driver_priv = master_priv;
+ return 0;
+}
+
+void radeon_master_destroy(struct drm_device *dev, struct drm_master *master)
+{
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
+
+ if (!master_priv)
+ return;
+
+ if (master_priv->sarea_priv &&
+ master_priv->sarea_priv->pfCurrentPage != 0)
+ radeon_cp_dispatch_flip(dev, master);
+
+ master_priv->sarea_priv = NULL;
+ if (master_priv->sarea)
+ drm_rmmap_locked(dev, master_priv->sarea);
+
+ drm_free(master_priv, sizeof(*master_priv), DRM_MEM_DRIVER);
+
+ master->driver_priv = NULL;
+}
+
/* Create mappings for registers and framebuffer so userland doesn't necessarily
* have to find them.
*/
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 71af746a4e47..fef207881f45 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -96,6 +96,8 @@ static struct drm_driver driver = {
.enable_vblank = radeon_enable_vblank,
.disable_vblank = radeon_disable_vblank,
.dri_library_name = dri_library_name,
+ .master_create = radeon_master_create,
+ .master_destroy = radeon_master_destroy,
.irq_preinstall = radeon_driver_irq_preinstall,
.irq_postinstall = radeon_driver_irq_postinstall,
.irq_uninstall = radeon_driver_irq_uninstall,
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h
index 3bbb871b25d5..490bc7ceef60 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.h
+++ b/drivers/gpu/drm/radeon/radeon_drv.h
@@ -226,9 +226,13 @@ struct radeon_virt_surface {
#define RADEON_FLUSH_EMITED (1 < 0)
#define RADEON_PURGE_EMITED (1 < 1)
+struct drm_radeon_master_private {
+ drm_local_map_t *sarea;
+ drm_radeon_sarea_t *sarea_priv;
+};
+
typedef struct drm_radeon_private {
drm_radeon_ring_buffer_t ring;
- drm_radeon_sarea_t *sarea_priv;
u32 fb_location;
u32 fb_size;
@@ -409,6 +413,9 @@ extern int radeon_driver_open(struct drm_device *dev,
extern long radeon_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg);
+extern int radeon_master_create(struct drm_device *dev, struct drm_master *master);
+extern void radeon_master_destroy(struct drm_device *dev, struct drm_master *master);
+extern void radeon_cp_dispatch_flip(struct drm_device *dev, struct drm_master *master);
/* r300_cmdbuf.c */
extern void r300_init_reg_flags(struct drm_device *dev);
@@ -1335,8 +1342,9 @@ do { \
} while (0)
#define VB_AGE_TEST_WITH_RETURN( dev_priv ) \
-do { \
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; \
+do { \
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv; \
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; \
if ( sarea_priv->last_dispatch >= RADEON_MAX_VB_AGE ) { \
int __ret = radeon_do_cp_idle( dev_priv ); \
if ( __ret ) return __ret; \
diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c
index 5d7153fcc7b0..ef940a079dcb 100644
--- a/drivers/gpu/drm/radeon/radeon_state.c
+++ b/drivers/gpu/drm/radeon/radeon_state.c
@@ -742,13 +742,14 @@ static struct {
*/
static void radeon_clear_box(drm_radeon_private_t * dev_priv,
+ struct drm_radeon_master_private *master_priv,
int x, int y, int w, int h, int r, int g, int b)
{
u32 color;
RING_LOCALS;
- x += dev_priv->sarea_priv->boxes[0].x1;
- y += dev_priv->sarea_priv->boxes[0].y1;
+ x += master_priv->sarea_priv->boxes[0].x1;
+ y += master_priv->sarea_priv->boxes[0].y1;
switch (dev_priv->color_fmt) {
case RADEON_COLOR_FORMAT_RGB565:
@@ -776,7 +777,7 @@ static void radeon_clear_box(drm_radeon_private_t * dev_priv,
RADEON_GMC_SRC_DATATYPE_COLOR |
RADEON_ROP3_P | RADEON_GMC_CLR_CMP_CNTL_DIS);
- if (dev_priv->sarea_priv->pfCurrentPage == 1) {
+ if (master_priv->sarea_priv->pfCurrentPage == 1) {
OUT_RING(dev_priv->front_pitch_offset);
} else {
OUT_RING(dev_priv->back_pitch_offset);
@@ -790,7 +791,7 @@ static void radeon_clear_box(drm_radeon_private_t * dev_priv,
ADVANCE_RING();
}
-static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv)
+static void radeon_cp_performance_boxes(drm_radeon_private_t *dev_priv, struct drm_radeon_master_private *master_priv)
{
/* Collapse various things into a wait flag -- trying to
* guess if userspase slept -- better just to have them tell us.
@@ -807,12 +808,12 @@ static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv)
/* Purple box for page flipping
*/
if (dev_priv->stats.boxes & RADEON_BOX_FLIP)
- radeon_clear_box(dev_priv, 4, 4, 8, 8, 255, 0, 255);
+ radeon_clear_box(dev_priv, master_priv, 4, 4, 8, 8, 255, 0, 255);
/* Red box if we have to wait for idle at any point
*/
if (dev_priv->stats.boxes & RADEON_BOX_WAIT_IDLE)
- radeon_clear_box(dev_priv, 16, 4, 8, 8, 255, 0, 0);
+ radeon_clear_box(dev_priv, master_priv, 16, 4, 8, 8, 255, 0, 0);
/* Blue box: lost context?
*/
@@ -820,12 +821,12 @@ static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv)
/* Yellow box for texture swaps
*/
if (dev_priv->stats.boxes & RADEON_BOX_TEXTURE_LOAD)
- radeon_clear_box(dev_priv, 40, 4, 8, 8, 255, 255, 0);
+ radeon_clear_box(dev_priv, master_priv, 40, 4, 8, 8, 255, 255, 0);
/* Green box if hardware never idles (as far as we can tell)
*/
if (!(dev_priv->stats.boxes & RADEON_BOX_DMA_IDLE))
- radeon_clear_box(dev_priv, 64, 4, 8, 8, 0, 255, 0);
+ radeon_clear_box(dev_priv, master_priv, 64, 4, 8, 8, 0, 255, 0);
/* Draw bars indicating number of buffers allocated
* (not a great measure, easily confused)
@@ -834,7 +835,7 @@ static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv)
if (dev_priv->stats.requested_bufs > 100)
dev_priv->stats.requested_bufs = 100;
- radeon_clear_box(dev_priv, 4, 16,
+ radeon_clear_box(dev_priv, master_priv, 4, 16,
dev_priv->stats.requested_bufs, 4,
196, 128, 128);
}
@@ -848,11 +849,13 @@ static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv)
*/
static void radeon_cp_dispatch_clear(struct drm_device * dev,
+ struct drm_master *master,
drm_radeon_clear_t * clear,
drm_radeon_clear_rect_t * depth_boxes)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
drm_radeon_depth_clear_t *depth_clear = &dev_priv->depth_clear;
int nbox = sarea_priv->nbox;
struct drm_clip_rect *pbox = sarea_priv->boxes;
@@ -864,7 +867,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
dev_priv->stats.clears++;
- if (dev_priv->sarea_priv->pfCurrentPage == 1) {
+ if (sarea_priv->pfCurrentPage == 1) {
unsigned int tmp = flags;
flags &= ~(RADEON_FRONT | RADEON_BACK);
@@ -890,7 +893,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
/* Make sure we restore the 3D state next time.
*/
- dev_priv->sarea_priv->ctx_owner = 0;
+ sarea_priv->ctx_owner = 0;
for (i = 0; i < nbox; i++) {
int x = pbox[i].x1;
@@ -967,7 +970,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
/* Make sure we restore the 3D state next time.
* we haven't touched any "normal" state - still need this?
*/
- dev_priv->sarea_priv->ctx_owner = 0;
+ sarea_priv->ctx_owner = 0;
if ((dev_priv->flags & RADEON_HAS_HIERZ)
&& (flags & RADEON_USE_HIERZ)) {
@@ -1214,7 +1217,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
/* Make sure we restore the 3D state next time.
*/
- dev_priv->sarea_priv->ctx_owner = 0;
+ sarea_priv->ctx_owner = 0;
for (i = 0; i < nbox; i++) {
@@ -1285,7 +1288,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
/* Make sure we restore the 3D state next time.
*/
- dev_priv->sarea_priv->ctx_owner = 0;
+ sarea_priv->ctx_owner = 0;
for (i = 0; i < nbox; i++) {
@@ -1328,20 +1331,21 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
* wait on this value before performing the clear ioctl. We
* need this because the card's so damned fast...
*/
- dev_priv->sarea_priv->last_clear++;
+ sarea_priv->last_clear++;
BEGIN_RING(4);
- RADEON_CLEAR_AGE(dev_priv->sarea_priv->last_clear);
+ RADEON_CLEAR_AGE(sarea_priv->last_clear);
RADEON_WAIT_UNTIL_IDLE();
ADVANCE_RING();
}
-static void radeon_cp_dispatch_swap(struct drm_device * dev)
+static void radeon_cp_dispatch_swap(struct drm_device *dev, struct drm_master *master)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
int nbox = sarea_priv->nbox;
struct drm_clip_rect *pbox = sarea_priv->boxes;
int i;
@@ -1351,7 +1355,7 @@ static void radeon_cp_dispatch_swap(struct drm_device * dev)
/* Do some trivial performance monitoring...
*/
if (dev_priv->do_boxes)
- radeon_cp_performance_boxes(dev_priv);
+ radeon_cp_performance_boxes(dev_priv, master_priv);
/* Wait for the 3D stream to idle before dispatching the bitblt.
* This will prevent data corruption between the two streams.
@@ -1385,7 +1389,7 @@ static void radeon_cp_dispatch_swap(struct drm_device * dev)
/* Make this work even if front & back are flipped:
*/
OUT_RING(CP_PACKET0(RADEON_SRC_PITCH_OFFSET, 1));
- if (dev_priv->sarea_priv->pfCurrentPage == 0) {
+ if (sarea_priv->pfCurrentPage == 0) {
OUT_RING(dev_priv->back_pitch_offset);
OUT_RING(dev_priv->front_pitch_offset);
} else {
@@ -1405,31 +1409,32 @@ static void radeon_cp_dispatch_swap(struct drm_device * dev)
* throttle the framerate by waiting for this value before
* performing the swapbuffer ioctl.
*/
- dev_priv->sarea_priv->last_frame++;
+ sarea_priv->last_frame++;
BEGIN_RING(4);
- RADEON_FRAME_AGE(dev_priv->sarea_priv->last_frame);
+ RADEON_FRAME_AGE(sarea_priv->last_frame);
RADEON_WAIT_UNTIL_2D_IDLE();
ADVANCE_RING();
}
-static void radeon_cp_dispatch_flip(struct drm_device * dev)
+void radeon_cp_dispatch_flip(struct drm_device *dev, struct drm_master *master)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- struct drm_sarea *sarea = (struct drm_sarea *) dev_priv->sarea->handle;
- int offset = (dev_priv->sarea_priv->pfCurrentPage == 1)
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
+ struct drm_sarea *sarea = (struct drm_sarea *)master_priv->sarea->handle;
+ int offset = (master_priv->sarea_priv->pfCurrentPage == 1)
? dev_priv->front_offset : dev_priv->back_offset;
RING_LOCALS;
DRM_DEBUG("pfCurrentPage=%d\n",
- dev_priv->sarea_priv->pfCurrentPage);
+ master_priv->sarea_priv->pfCurrentPage);
/* Do some trivial performance monitoring...
*/
if (dev_priv->do_boxes) {
dev_priv->stats.boxes |= RADEON_BOX_FLIP;
- radeon_cp_performance_boxes(dev_priv);
+ radeon_cp_performance_boxes(dev_priv, master_priv);
}
/* Update the frame offsets for both CRTCs
@@ -1441,7 +1446,7 @@ static void radeon_cp_dispatch_flip(struct drm_device * dev)
((sarea->frame.y * dev_priv->front_pitch +
sarea->frame.x * (dev_priv->color_fmt - 2)) & ~7)
+ offset);
- OUT_RING_REG(RADEON_CRTC2_OFFSET, dev_priv->sarea_priv->crtc2_base
+ OUT_RING_REG(RADEON_CRTC2_OFFSET, master_priv->sarea_priv->crtc2_base
+ offset);
ADVANCE_RING();
@@ -1450,13 +1455,13 @@ static void radeon_cp_dispatch_flip(struct drm_device * dev)
* throttle the framerate by waiting for this value before
* performing the swapbuffer ioctl.
*/
- dev_priv->sarea_priv->last_frame++;
- dev_priv->sarea_priv->pfCurrentPage =
- 1 - dev_priv->sarea_priv->pfCurrentPage;
+ master_priv->sarea_priv->last_frame++;
+ master_priv->sarea_priv->pfCurrentPage =
+ 1 - master_priv->sarea_priv->pfCurrentPage;
BEGIN_RING(2);
- RADEON_FRAME_AGE(dev_priv->sarea_priv->last_frame);
+ RADEON_FRAME_AGE(master_priv->sarea_priv->last_frame);
ADVANCE_RING();
}
@@ -1494,11 +1499,13 @@ typedef struct {
} drm_radeon_tcl_prim_t;
static void radeon_cp_dispatch_vertex(struct drm_device * dev,
+ struct drm_file *file_priv,
struct drm_buf * buf,
drm_radeon_tcl_prim_t * prim)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
int offset = dev_priv->gart_buffers_offset + buf->offset + prim->start;
int numverts = (int)prim->numverts;
int nbox = sarea_priv->nbox;
@@ -1539,13 +1546,14 @@ static void radeon_cp_dispatch_vertex(struct drm_device * dev,
} while (i < nbox);
}
-static void radeon_cp_discard_buffer(struct drm_device * dev, struct drm_buf * buf)
+static void radeon_cp_discard_buffer(struct drm_device *dev, struct drm_master *master, struct drm_buf *buf)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
RING_LOCALS;
- buf_priv->age = ++dev_priv->sarea_priv->last_dispatch;
+ buf_priv->age = ++master_priv->sarea_priv->last_dispatch;
/* Emit the vertex buffer age */
BEGIN_RING(2);
@@ -1590,12 +1598,14 @@ static void radeon_cp_dispatch_indirect(struct drm_device * dev,
}
}
-static void radeon_cp_dispatch_indices(struct drm_device * dev,
+static void radeon_cp_dispatch_indices(struct drm_device *dev,
+ struct drm_master *master,
struct drm_buf * elt_buf,
drm_radeon_tcl_prim_t * prim)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
int offset = dev_priv->gart_buffers_offset + prim->offset;
u32 *data;
int dwords;
@@ -1870,7 +1880,7 @@ static int radeon_cp_dispatch_texture(struct drm_device * dev,
ADVANCE_RING();
COMMIT_RING();
- radeon_cp_discard_buffer(dev, buf);
+ radeon_cp_discard_buffer(dev, file_priv->master, buf);
/* Update the input parameters for next time */
image->y += height;
@@ -2110,7 +2120,8 @@ static int radeon_surface_free(struct drm_device *dev, void *data, struct drm_fi
static int radeon_cp_clear(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
drm_radeon_clear_t *clear = data;
drm_radeon_clear_rect_t depth_boxes[RADEON_NR_SAREA_CLIPRECTS];
DRM_DEBUG("\n");
@@ -2126,7 +2137,7 @@ static int radeon_cp_clear(struct drm_device *dev, void *data, struct drm_file *
sarea_priv->nbox * sizeof(depth_boxes[0])))
return -EFAULT;
- radeon_cp_dispatch_clear(dev, clear, depth_boxes);
+ radeon_cp_dispatch_clear(dev, file_priv->master, clear, depth_boxes);
COMMIT_RING();
return 0;
@@ -2134,9 +2145,10 @@ static int radeon_cp_clear(struct drm_device *dev, void *data, struct drm_file *
/* Not sure why this isn't set all the time:
*/
-static int radeon_do_init_pageflip(struct drm_device * dev)
+static int radeon_do_init_pageflip(struct drm_device *dev, struct drm_master *master)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
RING_LOCALS;
DRM_DEBUG("\n");
@@ -2153,8 +2165,8 @@ static int radeon_do_init_pageflip(struct drm_device * dev)
dev_priv->page_flipping = 1;
- if (dev_priv->sarea_priv->pfCurrentPage != 1)
- dev_priv->sarea_priv->pfCurrentPage = 0;
+ if (master_priv->sarea_priv->pfCurrentPage != 1)
+ master_priv->sarea_priv->pfCurrentPage = 0;
return 0;
}
@@ -2172,9 +2184,9 @@ static int radeon_cp_flip(struct drm_device *dev, void *data, struct drm_file *f
RING_SPACE_TEST_WITH_RETURN(dev_priv);
if (!dev_priv->page_flipping)
- radeon_do_init_pageflip(dev);
+ radeon_do_init_pageflip(dev, file_priv->master);
- radeon_cp_dispatch_flip(dev);
+ radeon_cp_dispatch_flip(dev, file_priv->master);
COMMIT_RING();
return 0;
@@ -2183,7 +2195,9 @@ static int radeon_cp_flip(struct drm_device *dev, void *data, struct drm_file *f
static int radeon_cp_swap(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
+
DRM_DEBUG("\n");
LOCK_TEST_WITH_RETURN(dev, file_priv);
@@ -2193,8 +2207,8 @@ static int radeon_cp_swap(struct drm_device *dev, void *data, struct drm_file *f
if (sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS)
sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS;
- radeon_cp_dispatch_swap(dev);
- dev_priv->sarea_priv->ctx_owner = 0;
+ radeon_cp_dispatch_swap(dev, file_priv->master);
+ sarea_priv->ctx_owner = 0;
COMMIT_RING();
return 0;
@@ -2203,7 +2217,8 @@ static int radeon_cp_swap(struct drm_device *dev, void *data, struct drm_file *f
static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv;
struct drm_device_dma *dma = dev->dma;
struct drm_buf *buf;
drm_radeon_vertex_t *vertex = data;
@@ -2211,6 +2226,8 @@ static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file
LOCK_TEST_WITH_RETURN(dev, file_priv);
+ sarea_priv = master_priv->sarea_priv;
+
DRM_DEBUG("pid=%d index=%d count=%d discard=%d\n",
DRM_CURRENTPID, vertex->idx, vertex->count, vertex->discard);
@@ -2263,13 +2280,13 @@ static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file
prim.finish = vertex->count; /* unused */
prim.prim = vertex->prim;
prim.numverts = vertex->count;
- prim.vc_format = dev_priv->sarea_priv->vc_format;
+ prim.vc_format = sarea_priv->vc_format;
- radeon_cp_dispatch_vertex(dev, buf, &prim);
+ radeon_cp_dispatch_vertex(dev, file_priv, buf, &prim);
}
if (vertex->discard) {
- radeon_cp_discard_buffer(dev, buf);
+ radeon_cp_discard_buffer(dev, file_priv->master, buf);
}
COMMIT_RING();
@@ -2279,7 +2296,8 @@ static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file
static int radeon_cp_indices(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv;
struct drm_device_dma *dma = dev->dma;
struct drm_buf *buf;
drm_radeon_indices_t *elts = data;
@@ -2288,6 +2306,8 @@ static int radeon_cp_indices(struct drm_device *dev, void *data, struct drm_file
LOCK_TEST_WITH_RETURN(dev, file_priv);
+ sarea_priv = master_priv->sarea_priv;
+
DRM_DEBUG("pid=%d index=%d start=%d end=%d discard=%d\n",
DRM_CURRENTPID, elts->idx, elts->start, elts->end,
elts->discard);
@@ -2353,11 +2373,11 @@ static int radeon_cp_indices(struct drm_device *dev, void *data, struct drm_file
prim.prim = elts->prim;
prim.offset = 0; /* offset from start of dma buffers */
prim.numverts = RADEON_MAX_VB_VERTS; /* duh */
- prim.vc_format = dev_priv->sarea_priv->vc_format;
+ prim.vc_format = sarea_priv->vc_format;
- radeon_cp_dispatch_indices(dev, buf, &prim);
+ radeon_cp_dispatch_indices(dev, file_priv->master, buf, &prim);
if (elts->discard) {
- radeon_cp_discard_buffer(dev, buf);
+ radeon_cp_discard_buffer(dev, file_priv->master, buf);
}
COMMIT_RING();
@@ -2468,7 +2488,7 @@ static int radeon_cp_indirect(struct drm_device *dev, void *data, struct drm_fil
*/
radeon_cp_dispatch_indirect(dev, buf, indirect->start, indirect->end);
if (indirect->discard) {
- radeon_cp_discard_buffer(dev, buf);
+ radeon_cp_discard_buffer(dev, file_priv->master, buf);
}
COMMIT_RING();
@@ -2478,7 +2498,8 @@ static int radeon_cp_indirect(struct drm_device *dev, void *data, struct drm_fil
static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv;
struct drm_device_dma *dma = dev->dma;
struct drm_buf *buf;
drm_radeon_vertex2_t *vertex = data;
@@ -2487,6 +2508,8 @@ static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file
LOCK_TEST_WITH_RETURN(dev, file_priv);
+ sarea_priv = master_priv->sarea_priv;
+
DRM_DEBUG("pid=%d index=%d discard=%d\n",
DRM_CURRENTPID, vertex->idx, vertex->discard);
@@ -2547,12 +2570,12 @@ static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file
tclprim.offset = prim.numverts * 64;
tclprim.numverts = RADEON_MAX_VB_VERTS; /* duh */
- radeon_cp_dispatch_indices(dev, buf, &tclprim);
+ radeon_cp_dispatch_indices(dev, file_priv->master, buf, &tclprim);
} else {
tclprim.numverts = prim.numverts;
tclprim.offset = 0; /* not used */
- radeon_cp_dispatch_vertex(dev, buf, &tclprim);
+ radeon_cp_dispatch_vertex(dev, file_priv, buf, &tclprim);
}
if (sarea_priv->nbox == 1)
@@ -2560,7 +2583,7 @@ static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file
}
if (vertex->discard) {
- radeon_cp_discard_buffer(dev, buf);
+ radeon_cp_discard_buffer(dev, file_priv->master, buf);
}
COMMIT_RING();
@@ -2909,7 +2932,7 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file
goto err;
}
- radeon_cp_discard_buffer(dev, buf);
+ radeon_cp_discard_buffer(dev, file_priv->master, buf);
break;
case RADEON_CMD_PACKET3:
@@ -3020,7 +3043,7 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil
*/
case RADEON_PARAM_SAREA_HANDLE:
/* The lock is the first dword in the sarea. */
- value = (long)dev->lock.hw_lock;
+ /* no users of this parameter */
break;
#endif
case RADEON_PARAM_GART_TEX_HANDLE:
@@ -3064,6 +3087,7 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil
static int radeon_cp_setparam(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
drm_radeon_setparam_t *sp = data;
struct drm_radeon_driver_file_fields *radeon_priv;
@@ -3078,12 +3102,14 @@ static int radeon_cp_setparam(struct drm_device *dev, void *data, struct drm_fil
DRM_DEBUG("color tiling disabled\n");
dev_priv->front_pitch_offset &= ~RADEON_DST_TILE_MACRO;
dev_priv->back_pitch_offset &= ~RADEON_DST_TILE_MACRO;
- dev_priv->sarea_priv->tiling_enabled = 0;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->tiling_enabled = 0;
} else if (sp->value == 1) {
DRM_DEBUG("color tiling enabled\n");
dev_priv->front_pitch_offset |= RADEON_DST_TILE_MACRO;
dev_priv->back_pitch_offset |= RADEON_DST_TILE_MACRO;
- dev_priv->sarea_priv->tiling_enabled = 1;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->tiling_enabled = 1;
}
break;
case RADEON_SETPARAM_PCIGART_LOCATION:
@@ -3129,14 +3155,6 @@ void radeon_driver_preclose(struct drm_device *dev, struct drm_file *file_priv)
void radeon_driver_lastclose(struct drm_device *dev)
{
- if (dev->dev_private) {
- drm_radeon_private_t *dev_priv = dev->dev_private;
-
- if (dev_priv->sarea_priv &&
- dev_priv->sarea_priv->pfCurrentPage != 0)
- radeon_cp_dispatch_flip(dev);
- }
-
radeon_do_release(dev);
}
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index 906f9b9d715d..587f5b2380d4 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -1016,7 +1016,7 @@ static int i2c_pxa_probe(struct platform_device *dev)
snprintf(i2c->adap.name, sizeof(i2c->adap.name), "pxa_i2c-i2c.%u",
i2c->adap.nr);
- i2c->clk = clk_get(&dev->dev, "I2CCLK");
+ i2c->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(i2c->clk)) {
ret = PTR_ERR(i2c->clk);
goto eclk;
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index b7434d24904e..c39079f9c73f 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -40,8 +40,8 @@
#include <asm/io.h>
#include <mach/regs-gpio.h>
-#include <asm/plat-s3c/regs-iic.h>
-#include <asm/plat-s3c/iic.h>
+#include <plat/regs-iic.h>
+#include <plat/iic.h>
/* i2c controller state */
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index e6857e01d1ba..c9f21e3d4ead 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -62,6 +62,9 @@ config IDE_TIMINGS
config IDE_ATAPI
bool
+config IDE_LEGACY
+ bool
+
config BLK_DEV_IDE_SATA
bool "Support for SATA (deprecated; conflicts with libata SATA driver)"
default n
@@ -724,7 +727,7 @@ config BLK_DEV_IDE_TX4939
config IDE_ARM
tristate "ARM IDE support"
- depends on ARM && (ARCH_CLPS7500 || ARCH_RPC || ARCH_SHARK)
+ depends on ARM && (ARCH_RPC || ARCH_SHARK)
default y
config BLK_DEV_IDE_ICSIDE
@@ -856,6 +859,7 @@ config BLK_DEV_4DRIVES
config BLK_DEV_ALI14XX
tristate "ALI M14xx support"
select IDE_TIMINGS
+ select IDE_LEGACY
help
This driver is enabled at runtime using the "ali14xx.probe" kernel
boot parameter. It enables support for the secondary IDE interface
@@ -866,6 +870,7 @@ config BLK_DEV_ALI14XX
config BLK_DEV_DTC2278
tristate "DTC-2278 support"
+ select IDE_LEGACY
help
This driver is enabled at runtime using the "dtc2278.probe" kernel
boot parameter. It enables support for the secondary IDE interface
@@ -876,6 +881,7 @@ config BLK_DEV_DTC2278
config BLK_DEV_HT6560B
tristate "Holtek HT6560B support"
select IDE_TIMINGS
+ select IDE_LEGACY
help
This driver is enabled at runtime using the "ht6560b.probe" kernel
boot parameter. It enables support for the secondary IDE interface
@@ -886,6 +892,7 @@ config BLK_DEV_HT6560B
config BLK_DEV_QD65XX
tristate "QDI QD65xx support"
select IDE_TIMINGS
+ select IDE_LEGACY
help
This driver is enabled at runtime using the "qd65xx.probe" kernel
boot parameter. It permits faster I/O speeds to be set. See the
@@ -894,6 +901,7 @@ config BLK_DEV_QD65XX
config BLK_DEV_UMC8672
tristate "UMC-8672 support"
+ select IDE_LEGACY
help
This driver is enabled at runtime using the "umc8672.probe" kernel
boot parameter. It enables support for the secondary IDE interface
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
index 7818d402b188..177e3f8523ed 100644
--- a/drivers/ide/Makefile
+++ b/drivers/ide/Makefile
@@ -5,7 +5,7 @@
EXTRA_CFLAGS += -Idrivers/ide
ide-core-y += ide.o ide-ioctls.o ide-io.o ide-iops.o ide-lib.o ide-probe.o \
- ide-taskfile.o ide-park.o ide-pio-blacklist.o
+ ide-taskfile.o ide-pm.o ide-park.o ide-pio-blacklist.o
# core IDE code
ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o
@@ -15,6 +15,7 @@ ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o
ide-core-$(CONFIG_BLK_DEV_IDEDMA_SFF) += ide-dma-sff.o
ide-core-$(CONFIG_IDE_PROC_FS) += ide-proc.o
ide-core-$(CONFIG_BLK_DEV_IDEACPI) += ide-acpi.o
+ide-core-$(CONFIG_IDE_LEGACY) += ide-legacy.o
obj-$(CONFIG_IDE) += ide-core.o
diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c
index 935385c77e06..3623bf013bcf 100644
--- a/drivers/ide/cmd64x.c
+++ b/drivers/ide/cmd64x.c
@@ -424,10 +424,10 @@ static const struct ide_port_info cmd64x_chipsets[] __devinitdata = {
.name = DRV_NAME,
.init_chipset = init_chipset_cmd64x,
.enablebits = {{0x51,0x04,0x04}, {0x51,0x08,0x08}},
- .chipset = ide_cmd646,
.port_ops = &cmd64x_port_ops,
.dma_ops = &cmd648_dma_ops,
- .host_flags = IDE_HFLAG_ABUSE_PREFETCH,
+ .host_flags = IDE_HFLAG_SERIALIZE |
+ IDE_HFLAG_ABUSE_PREFETCH,
.pio_mask = ATA_PIO5,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA2,
diff --git a/drivers/ide/cy82c693.c b/drivers/ide/cy82c693.c
index 5297f07d2933..d37baf8ecc5f 100644
--- a/drivers/ide/cy82c693.c
+++ b/drivers/ide/cy82c693.c
@@ -292,7 +292,6 @@ static const struct ide_port_info cy82c693_chipset __devinitdata = {
.name = DRV_NAME,
.init_iops = init_iops_cy82c693,
.port_ops = &cy82c693_port_ops,
- .chipset = ide_cy82c693,
.host_flags = IDE_HFLAG_SINGLE,
.pio_mask = ATA_PIO4,
.swdma_mask = ATA_SWDMA2,
diff --git a/drivers/ide/gayle.c b/drivers/ide/gayle.c
index 691506886561..59bd0be9dcb3 100644
--- a/drivers/ide/gayle.c
+++ b/drivers/ide/gayle.c
@@ -117,6 +117,10 @@ static void __init gayle_setup_ports(hw_regs_t *hw, unsigned long base,
hw->chipset = ide_generic;
}
+static const struct ide_port_info gayle_port_info = {
+ .host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_NO_DMA,
+};
+
/*
* Probe for a Gayle IDE interface (and optionally for an IDE doubler)
*/
@@ -178,7 +182,7 @@ found:
hws[i] = &hw[i];
}
- rc = ide_host_add(NULL, hws, NULL);
+ rc = ide_host_add(&gayle_port_info, hws, NULL);
if (rc)
release_mem_region(res_start, res_n);
diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c
index f5afd46ed51c..b18e10d99d2e 100644
--- a/drivers/ide/hpt366.c
+++ b/drivers/ide/hpt366.c
@@ -135,7 +135,6 @@
/* various tuning parameters */
#define HPT_RESET_STATE_ENGINE
#undef HPT_DELAY_INTERRUPT
-#define HPT_SERIALIZE_IO 0
static const char *quirk_drives[] = {
"QUANTUM FIREBALLlct08 08",
@@ -1288,7 +1287,6 @@ static u8 hpt3xx_cable_detect(ide_hwif_t *hwif)
static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
{
struct hpt_info *info = hpt3xx_get_info(hwif->dev);
- int serialize = HPT_SERIALIZE_IO;
u8 chip_type = info->chip_type;
/* Cache the channel's MISC. control registers' offset */
@@ -1305,13 +1303,9 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
* Clock is shared between the channels,
* so we'll have to serialize them... :-(
*/
- serialize = 1;
+ hwif->host->host_flags |= IDE_HFLAG_SERIALIZE;
hwif->rw_disk = &hpt3xxn_rw_disk;
}
-
- /* Serialize access to this device if needed */
- if (serialize && hwif->mate)
- hwif->serialized = hwif->mate->serialized = 1;
}
static int __devinit init_dma_hpt366(ide_hwif_t *hwif,
diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c
index 244a8a052ce8..fd4a36433050 100644
--- a/drivers/ide/ide-acpi.c
+++ b/drivers/ide/ide-acpi.c
@@ -615,10 +615,10 @@ void ide_acpi_push_timing(ide_hwif_t *hwif)
in_params[0].buffer.length = sizeof(struct GTM_buffer);
in_params[0].buffer.pointer = (u8 *)&hwif->acpidata->gtm;
in_params[1].type = ACPI_TYPE_BUFFER;
- in_params[1].buffer.length = sizeof(ATA_ID_WORDS * 2);
+ in_params[1].buffer.length = ATA_ID_WORDS * 2;
in_params[1].buffer.pointer = (u8 *)&master->idbuff;
in_params[2].type = ACPI_TYPE_BUFFER;
- in_params[2].buffer.length = sizeof(ATA_ID_WORDS * 2);
+ in_params[2].buffer.length = ATA_ID_WORDS * 2;
in_params[2].buffer.pointer = (u8 *)&slave->idbuff;
/* Output buffer: _STM has no output */
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index 42ab6d8715f2..5daa4dd1b018 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -262,7 +262,6 @@ static void cdrom_end_request(ide_drive_t *drive, int uptodate)
struct request *failed = (struct request *) rq->buffer;
struct cdrom_info *info = drive->driver_data;
void *sense = &info->sense_data;
- unsigned long flags;
if (failed) {
if (failed->sense) {
@@ -278,11 +277,9 @@ static void cdrom_end_request(ide_drive_t *drive, int uptodate)
failed->hard_nr_sectors))
BUG();
} else {
- spin_lock_irqsave(&ide_lock, flags);
- if (__blk_end_request(failed, -EIO,
- failed->data_len))
+ if (blk_end_request(failed, -EIO,
+ failed->data_len))
BUG();
- spin_unlock_irqrestore(&ide_lock, flags);
}
} else
cdrom_analyze_sense_data(drive, NULL, sense);
@@ -317,7 +314,8 @@ static void ide_dump_status_no_sense(ide_drive_t *drive, const char *msg, u8 st)
static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
{
ide_hwif_t *hwif = drive->hwif;
- struct request *rq = hwif->hwgroup->rq;
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
+ struct request *rq = hwgroup->rq;
int stat, err, sense_key;
/* check for errors */
@@ -426,16 +424,17 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
if (time_after(jiffies, info->write_timeout))
do_end_request = 1;
else {
+ struct request_queue *q = drive->queue;
unsigned long flags;
/*
* take a breather relying on the unplug
* timer to kick us again
*/
- spin_lock_irqsave(&ide_lock, flags);
- blk_plug_device(drive->queue);
- spin_unlock_irqrestore(&ide_lock,
- flags);
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_plug_device(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
return 1;
}
}
@@ -504,12 +503,14 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
end_request:
if (stat & ATA_ERR) {
+ struct request_queue *q = drive->queue;
unsigned long flags;
- spin_lock_irqsave(&ide_lock, flags);
+ spin_lock_irqsave(q->queue_lock, flags);
blkdev_dequeue_request(rq);
- HWGROUP(drive)->rq = NULL;
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ hwgroup->rq = NULL;
cdrom_queue_request_sense(drive, rq->sense, rq);
} else
@@ -773,52 +774,6 @@ static ide_startstop_t cdrom_start_rw_cont(ide_drive_t *drive)
return cdrom_transfer_packet_command(drive, rq, cdrom_newpc_intr);
}
-#define IDECD_SEEK_THRESHOLD (1000) /* 1000 blocks */
-#define IDECD_SEEK_TIMER (5 * WAIT_MIN_SLEEP) /* 100 ms */
-#define IDECD_SEEK_TIMEOUT (2 * WAIT_CMD) /* 20 sec */
-
-static ide_startstop_t cdrom_seek_intr(ide_drive_t *drive)
-{
- struct cdrom_info *info = drive->driver_data;
- int stat;
- static int retry = 10;
-
- ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
-
- if (cdrom_decode_status(drive, 0, &stat))
- return ide_stopped;
-
- drive->atapi_flags |= IDE_AFLAG_SEEKING;
-
- if (retry && time_after(jiffies, info->start_seek + IDECD_SEEK_TIMER)) {
- if (--retry == 0)
- drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
- }
- return ide_stopped;
-}
-
-static void ide_cd_prepare_seek_request(ide_drive_t *drive, struct request *rq)
-{
- sector_t frame = rq->sector;
-
- ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
-
- sector_div(frame, queue_hardsect_size(drive->queue) >> SECTOR_BITS);
-
- memset(rq->cmd, 0, BLK_MAX_CDB);
- rq->cmd[0] = GPCMD_SEEK;
- put_unaligned(cpu_to_be32(frame), (unsigned int *) &rq->cmd[2]);
-
- rq->timeout = ATAPI_WAIT_PC;
-}
-
-static ide_startstop_t cdrom_start_seek_continuation(ide_drive_t *drive)
-{
- struct request *rq = drive->hwif->hwgroup->rq;
-
- return cdrom_transfer_packet_command(drive, rq, &cdrom_seek_intr);
-}
-
/*
* Fix up a possibly partially-processed request so that we can start it over
* entirely, or even put it back on the request queue.
@@ -950,7 +905,8 @@ static int cdrom_newpc_intr_dummy_cb(struct request *rq)
static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
- struct request *rq = HWGROUP(drive)->rq;
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
+ struct request *rq = hwgroup->rq;
xfer_func_t *xferfunc;
ide_expiry_t *expiry = NULL;
int dma_error = 0, dma, stat, thislen, uptodate = 0;
@@ -1148,17 +1104,15 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
end_request:
if (blk_pc_request(rq)) {
- unsigned long flags;
unsigned int dlen = rq->data_len;
if (dma)
rq->data_len = 0;
- spin_lock_irqsave(&ide_lock, flags);
- if (__blk_end_request(rq, 0, dlen))
+ if (blk_end_request(rq, 0, dlen))
BUG();
- HWGROUP(drive)->rq = NULL;
- spin_unlock_irqrestore(&ide_lock, flags);
+
+ hwgroup->rq = NULL;
} else {
if (!uptodate)
rq->cmd_flags |= REQ_FAILED;
@@ -1260,7 +1214,6 @@ static void cdrom_do_block_pc(ide_drive_t *drive, struct request *rq)
static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq,
sector_t block)
{
- struct cdrom_info *info = drive->driver_data;
ide_handler_t *fn;
int xferlen;
@@ -1270,44 +1223,14 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq,
(unsigned long long)block);
if (blk_fs_request(rq)) {
- if (drive->atapi_flags & IDE_AFLAG_SEEKING) {
- ide_hwif_t *hwif = drive->hwif;
- unsigned long elapsed = jiffies - info->start_seek;
- int stat = hwif->tp_ops->read_status(hwif);
-
- if ((stat & ATA_DSC) != ATA_DSC) {
- if (elapsed < IDECD_SEEK_TIMEOUT) {
- ide_stall_queue(drive,
- IDECD_SEEK_TIMER);
- return ide_stopped;
- }
- printk(KERN_ERR PFX "%s: DSC timeout\n",
- drive->name);
- }
- drive->atapi_flags &= ~IDE_AFLAG_SEEKING;
- }
- if (rq_data_dir(rq) == READ &&
- IDE_LARGE_SEEK(info->last_block, block,
- IDECD_SEEK_THRESHOLD) &&
- (drive->dev_flags & IDE_DFLAG_DSC_OVERLAP)) {
- xferlen = 0;
- fn = cdrom_start_seek_continuation;
+ xferlen = 32768;
+ fn = cdrom_start_rw_cont;
- drive->dma = 0;
- info->start_seek = jiffies;
-
- ide_cd_prepare_seek_request(drive, rq);
- } else {
- xferlen = 32768;
- fn = cdrom_start_rw_cont;
-
- if (cdrom_start_rw(drive, rq) == ide_stopped)
- return ide_stopped;
+ if (cdrom_start_rw(drive, rq) == ide_stopped)
+ return ide_stopped;
- if (ide_cd_prepare_rw_request(drive, rq) == ide_stopped)
- return ide_stopped;
- }
- info->last_block = block;
+ if (ide_cd_prepare_rw_request(drive, rq) == ide_stopped)
+ return ide_stopped;
} else if (blk_sense_request(rq) || blk_pc_request(rq) ||
rq->cmd_type == REQ_TYPE_ATA_PC) {
xferlen = rq->data_len;
@@ -1908,13 +1831,6 @@ static ide_proc_entry_t idecd_proc[] = {
{ NULL, 0, NULL, NULL }
};
-ide_devset_rw_flag(dsc_overlap, IDE_DFLAG_DSC_OVERLAP);
-
-static const struct ide_proc_devset idecd_settings[] = {
- IDE_PROC_DEVSET(dsc_overlap, 0, 1),
- { 0 },
-};
-
static ide_proc_entry_t *ide_cd_proc_entries(ide_drive_t *drive)
{
return idecd_proc;
@@ -1922,7 +1838,7 @@ static ide_proc_entry_t *ide_cd_proc_entries(ide_drive_t *drive)
static const struct ide_proc_devset *ide_cd_proc_devsets(ide_drive_t *drive)
{
- return idecd_settings;
+ return NULL;
}
#endif
@@ -2022,11 +1938,6 @@ static int ide_cdrom_setup(ide_drive_t *drive)
/* set correct block size */
blk_queue_hardsect_size(drive->queue, CD_FRAMESIZE);
- if (drive->next != drive)
- drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP;
- else
- drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
-
if (ide_cdrom_register(drive, nslots)) {
printk(KERN_ERR PFX "%s: %s failed to register device with the"
" cdrom driver.\n", drive->name, __func__);
@@ -2063,7 +1974,6 @@ static void ide_cd_release(struct kref *kref)
kfree(info->toc);
if (devinfo->handle == drive)
unregister_cdrom(devinfo);
- drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
drive->driver_data = NULL;
blk_queue_prep_rq(drive->queue, NULL);
g->private_data = NULL;
diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h
index 5882b9a9ea8b..d5ce3362dbd1 100644
--- a/drivers/ide/ide-cd.h
+++ b/drivers/ide/ide-cd.h
@@ -88,8 +88,6 @@ struct cdrom_info {
struct request_sense sense_data;
struct request request_sense_request;
- unsigned long last_block;
- unsigned long start_seek;
u8 max_speed; /* Max speed of the drive. */
u8 current_speed; /* Current speed of the drive. */
diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c
index cac431f0df17..f6d2d44d8a9a 100644
--- a/drivers/ide/ide-dma-sff.c
+++ b/drivers/ide/ide-dma-sff.c
@@ -98,10 +98,10 @@ int ide_build_dmatable(ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = drive->hwif;
__le32 *table = (__le32 *)hwif->dmatable_cpu;
- unsigned int is_trm290 = (hwif->chipset == ide_trm290) ? 1 : 0;
unsigned int count = 0;
int i;
struct scatterlist *sg;
+ u8 is_trm290 = !!(hwif->host_flags & IDE_HFLAG_TRM290);
hwif->sg_nents = ide_build_sglist(drive, rq);
if (hwif->sg_nents == 0)
@@ -176,15 +176,10 @@ int ide_dma_setup(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
struct request *rq = hwif->hwgroup->rq;
- unsigned int reading;
+ unsigned int reading = rq_data_dir(rq) ? 0 : ATA_DMA_WR;
u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
u8 dma_stat;
- if (rq_data_dir(rq))
- reading = 0;
- else
- reading = 1 << 3;
-
/* fall back to pio! */
if (!ide_build_dmatable(drive, rq)) {
ide_map_sg(drive, rq);
@@ -209,10 +204,11 @@ int ide_dma_setup(ide_drive_t *drive)
/* clear INTR & ERROR flags */
if (mmio)
- writeb(dma_stat | 6,
+ writeb(dma_stat | ATA_DMA_ERR | ATA_DMA_INTR,
(void __iomem *)(hwif->dma_base + ATA_DMA_STATUS));
else
- outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS);
+ outb(dma_stat | ATA_DMA_ERR | ATA_DMA_INTR,
+ hwif->dma_base + ATA_DMA_STATUS);
drive->waiting_for_dma = 1;
return 0;
@@ -246,14 +242,13 @@ static int dma_timer_expiry(ide_drive_t *drive)
hwif->hwgroup->expiry = NULL; /* one free ride for now */
- /* 1 dmaing, 2 error, 4 intr */
- if (dma_stat & 2) /* ERROR */
+ if (dma_stat & ATA_DMA_ERR) /* ERROR */
return -1;
- if (dma_stat & 1) /* DMAing */
+ if (dma_stat & ATA_DMA_ACTIVE) /* DMAing */
return WAIT_CMD;
- if (dma_stat & 4) /* Got an Interrupt */
+ if (dma_stat & ATA_DMA_INTR) /* Got an Interrupt */
return WAIT_CMD;
return 0; /* Status is unknown -- reset the bus */
@@ -279,12 +274,11 @@ void ide_dma_start(ide_drive_t *drive)
*/
if (hwif->host_flags & IDE_HFLAG_MMIO) {
dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
- /* start DMA */
- writeb(dma_cmd | 1,
+ writeb(dma_cmd | ATA_DMA_START,
(void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
} else {
dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
- outb(dma_cmd | 1, hwif->dma_base + ATA_DMA_CMD);
+ outb(dma_cmd | ATA_DMA_START, hwif->dma_base + ATA_DMA_CMD);
}
wmb();
@@ -296,19 +290,18 @@ int ide_dma_end(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
- u8 dma_stat = 0, dma_cmd = 0;
+ u8 dma_stat = 0, dma_cmd = 0, mask;
drive->waiting_for_dma = 0;
+ /* stop DMA */
if (mmio) {
- /* get DMA command mode */
dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
- /* stop DMA */
- writeb(dma_cmd & ~1,
+ writeb(dma_cmd & ~ATA_DMA_START,
(void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
} else {
dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
- outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD);
+ outb(dma_cmd & ~ATA_DMA_START, hwif->dma_base + ATA_DMA_CMD);
}
/* get DMA status */
@@ -316,16 +309,21 @@ int ide_dma_end(ide_drive_t *drive)
if (mmio)
/* clear the INTR & ERROR bits */
- writeb(dma_stat | 6,
+ writeb(dma_stat | ATA_DMA_ERR | ATA_DMA_INTR,
(void __iomem *)(hwif->dma_base + ATA_DMA_STATUS));
else
- outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS);
+ outb(dma_stat | ATA_DMA_ERR | ATA_DMA_INTR,
+ hwif->dma_base + ATA_DMA_STATUS);
/* purge DMA mappings */
ide_destroy_dmatable(drive);
- /* verify good DMA status */
wmb();
- return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;
+
+ /* verify good DMA status */
+ mask = ATA_DMA_ACTIVE | ATA_DMA_ERR | ATA_DMA_INTR;
+ if ((dma_stat & mask) != ATA_DMA_INTR)
+ return 0x10 | dma_stat;
+ return 0;
}
EXPORT_SYMBOL_GPL(ide_dma_end);
@@ -335,11 +333,7 @@ int ide_dma_test_irq(ide_drive_t *drive)
ide_hwif_t *hwif = drive->hwif;
u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
- /* return 1 if INTR asserted */
- if ((dma_stat & 4) == 4)
- return 1;
-
- return 0;
+ return (dma_stat & ATA_DMA_INTR) ? 1 : 0;
}
EXPORT_SYMBOL_GPL(ide_dma_test_irq);
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index cc35d6dbd410..ecacc008fdaf 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -84,11 +84,11 @@ static int __ide_end_request(ide_drive_t *drive, struct request *rq,
ide_dma_on(drive);
}
- if (!__blk_end_request(rq, error, nr_bytes)) {
- if (dequeue)
- HWGROUP(drive)->rq = NULL;
+ if (!blk_end_request(rq, error, nr_bytes))
ret = 0;
- }
+
+ if (ret == 0 && dequeue)
+ drive->hwif->hwgroup->rq = NULL;
return ret;
}
@@ -107,16 +107,7 @@ static int __ide_end_request(ide_drive_t *drive, struct request *rq,
int ide_end_request (ide_drive_t *drive, int uptodate, int nr_sectors)
{
unsigned int nr_bytes = nr_sectors << 9;
- struct request *rq;
- unsigned long flags;
- int ret = 1;
-
- /*
- * room for locking improvements here, the calls below don't
- * need the queue lock held at all
- */
- spin_lock_irqsave(&ide_lock, flags);
- rq = HWGROUP(drive)->rq;
+ struct request *rq = drive->hwif->hwgroup->rq;
if (!nr_bytes) {
if (blk_pc_request(rq))
@@ -125,105 +116,10 @@ int ide_end_request (ide_drive_t *drive, int uptodate, int nr_sectors)
nr_bytes = rq->hard_cur_sectors << 9;
}
- ret = __ide_end_request(drive, rq, uptodate, nr_bytes, 1);
-
- spin_unlock_irqrestore(&ide_lock, flags);
- return ret;
+ return __ide_end_request(drive, rq, uptodate, nr_bytes, 1);
}
EXPORT_SYMBOL(ide_end_request);
-static void ide_complete_power_step(ide_drive_t *drive, struct request *rq)
-{
- struct request_pm_state *pm = rq->data;
-
-#ifdef DEBUG_PM
- printk(KERN_INFO "%s: complete_power_step(step: %d)\n",
- drive->name, pm->pm_step);
-#endif
- if (drive->media != ide_disk)
- return;
-
- switch (pm->pm_step) {
- case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */
- if (pm->pm_state == PM_EVENT_FREEZE)
- pm->pm_step = IDE_PM_COMPLETED;
- else
- pm->pm_step = IDE_PM_STANDBY;
- break;
- case IDE_PM_STANDBY: /* Suspend step 2 (standby) */
- pm->pm_step = IDE_PM_COMPLETED;
- break;
- case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */
- pm->pm_step = IDE_PM_IDLE;
- break;
- case IDE_PM_IDLE: /* Resume step 2 (idle)*/
- pm->pm_step = IDE_PM_RESTORE_DMA;
- break;
- }
-}
-
-static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq)
-{
- struct request_pm_state *pm = rq->data;
- ide_task_t *args = rq->special;
-
- memset(args, 0, sizeof(*args));
-
- switch (pm->pm_step) {
- case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */
- if (drive->media != ide_disk)
- break;
- /* Not supported? Switch to next step now. */
- if (ata_id_flush_enabled(drive->id) == 0 ||
- (drive->dev_flags & IDE_DFLAG_WCACHE) == 0) {
- ide_complete_power_step(drive, rq);
- return ide_stopped;
- }
- if (ata_id_flush_ext_enabled(drive->id))
- args->tf.command = ATA_CMD_FLUSH_EXT;
- else
- args->tf.command = ATA_CMD_FLUSH;
- goto out_do_tf;
- case IDE_PM_STANDBY: /* Suspend step 2 (standby) */
- args->tf.command = ATA_CMD_STANDBYNOW1;
- goto out_do_tf;
- case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */
- ide_set_max_pio(drive);
- /*
- * skip IDE_PM_IDLE for ATAPI devices
- */
- if (drive->media != ide_disk)
- pm->pm_step = IDE_PM_RESTORE_DMA;
- else
- ide_complete_power_step(drive, rq);
- return ide_stopped;
- case IDE_PM_IDLE: /* Resume step 2 (idle) */
- args->tf.command = ATA_CMD_IDLEIMMEDIATE;
- goto out_do_tf;
- case IDE_PM_RESTORE_DMA: /* Resume step 3 (restore DMA) */
- /*
- * Right now, all we do is call ide_set_dma(drive),
- * we could be smarter and check for current xfer_speed
- * in struct drive etc...
- */
- if (drive->hwif->dma_ops == NULL)
- break;
- /*
- * TODO: respect IDE_DFLAG_USING_DMA
- */
- ide_set_dma(drive);
- break;
- }
-
- pm->pm_step = IDE_PM_COMPLETED;
- return ide_stopped;
-
-out_do_tf:
- args->tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
- args->data_phase = TASKFILE_NO_DATA;
- return do_rw_taskfile(drive, args);
-}
-
/**
* ide_end_dequeued_request - complete an IDE I/O
* @drive: IDE device for the I/O
@@ -242,48 +138,12 @@ out_do_tf:
int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq,
int uptodate, int nr_sectors)
{
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&ide_lock, flags);
BUG_ON(!blk_rq_started(rq));
- ret = __ide_end_request(drive, rq, uptodate, nr_sectors << 9, 0);
- spin_unlock_irqrestore(&ide_lock, flags);
- return ret;
+ return __ide_end_request(drive, rq, uptodate, nr_sectors << 9, 0);
}
EXPORT_SYMBOL_GPL(ide_end_dequeued_request);
-
-/**
- * ide_complete_pm_request - end the current Power Management request
- * @drive: target drive
- * @rq: request
- *
- * This function cleans up the current PM request and stops the queue
- * if necessary.
- */
-static void ide_complete_pm_request (ide_drive_t *drive, struct request *rq)
-{
- unsigned long flags;
-
-#ifdef DEBUG_PM
- printk("%s: completing PM request, %s\n", drive->name,
- blk_pm_suspend_request(rq) ? "suspend" : "resume");
-#endif
- spin_lock_irqsave(&ide_lock, flags);
- if (blk_pm_suspend_request(rq)) {
- blk_stop_queue(drive->queue);
- } else {
- drive->dev_flags &= ~IDE_DFLAG_BLOCKED;
- blk_start_queue(drive->queue);
- }
- HWGROUP(drive)->rq = NULL;
- if (__blk_end_request(rq, 0, 0))
- BUG();
- spin_unlock_irqrestore(&ide_lock, flags);
-}
-
/**
* ide_end_drive_cmd - end an explicit drive command
* @drive: command
@@ -300,19 +160,12 @@ static void ide_complete_pm_request (ide_drive_t *drive, struct request *rq)
void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err)
{
- unsigned long flags;
- struct request *rq;
-
- spin_lock_irqsave(&ide_lock, flags);
- rq = HWGROUP(drive)->rq;
- spin_unlock_irqrestore(&ide_lock, flags);
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
+ struct request *rq = hwgroup->rq;
if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
ide_task_t *task = (ide_task_t *)rq->special;
- if (rq->errors == 0)
- rq->errors = !OK_STAT(stat, ATA_DRDY, BAD_STAT);
-
if (task) {
struct ide_taskfile *tf = &task->tf;
@@ -333,15 +186,14 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err)
return;
}
- spin_lock_irqsave(&ide_lock, flags);
- HWGROUP(drive)->rq = NULL;
+ hwgroup->rq = NULL;
+
rq->errors = err;
- if (unlikely(__blk_end_request(rq, (rq->errors ? -EIO : 0),
- blk_rq_bytes(rq))))
+
+ if (unlikely(blk_end_request(rq, (rq->errors ? -EIO : 0),
+ blk_rq_bytes(rq))))
BUG();
- spin_unlock_irqrestore(&ide_lock, flags);
}
-
EXPORT_SYMBOL(ide_end_drive_cmd);
static void ide_kill_rq(ide_drive_t *drive, struct request *rq)
@@ -720,40 +572,6 @@ static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq)
}
}
-static void ide_check_pm_state(ide_drive_t *drive, struct request *rq)
-{
- struct request_pm_state *pm = rq->data;
-
- if (blk_pm_suspend_request(rq) &&
- pm->pm_step == IDE_PM_START_SUSPEND)
- /* Mark drive blocked when starting the suspend sequence. */
- drive->dev_flags |= IDE_DFLAG_BLOCKED;
- else if (blk_pm_resume_request(rq) &&
- pm->pm_step == IDE_PM_START_RESUME) {
- /*
- * The first thing we do on wakeup is to wait for BSY bit to
- * go away (with a looong timeout) as a drive on this hwif may
- * just be POSTing itself.
- * We do that before even selecting as the "other" device on
- * the bus may be broken enough to walk on our toes at this
- * point.
- */
- ide_hwif_t *hwif = drive->hwif;
- int rc;
-#ifdef DEBUG_PM
- printk("%s: Wakeup request inited, waiting for !BSY...\n", drive->name);
-#endif
- rc = ide_wait_not_busy(hwif, 35000);
- if (rc)
- printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name);
- SELECT_DRIVE(drive);
- hwif->tp_ops->set_irq(hwif, 1);
- rc = ide_wait_not_busy(hwif, 100000);
- if (rc)
- printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name);
- }
-}
-
/**
* start_request - start of I/O and command issuing for IDE
*
@@ -927,7 +745,7 @@ repeat:
/*
* Issue a new request to a drive from hwgroup
- * Caller must have already done spin_lock_irqsave(&ide_lock, ..);
+ * Caller must have already done spin_lock_irqsave(&hwgroup->lock, ..);
*
* A hwgroup is a serialized group of IDE interfaces. Usually there is
* exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640)
@@ -939,7 +757,7 @@ repeat:
* possibly along with many other devices. This is especially common in
* PCI-based systems with off-board IDE controller cards.
*
- * The IDE driver uses the single global ide_lock spinlock to protect
+ * The IDE driver uses a per-hwgroup spinlock to protect
* access to the request queues, and to protect the hwgroup->busy flag.
*
* The first thread into the driver for a particular hwgroup sets the
@@ -955,7 +773,7 @@ repeat:
* will start the next request from the queue. If no more work remains,
* the driver will clear the hwgroup->busy flag and exit.
*
- * The ide_lock (spinlock) is used to protect all access to the
+ * The per-hwgroup spinlock is used to protect all access to the
* hwgroup->busy flag, but is otherwise not needed for most processing in
* the driver. This makes the driver much more friendlier to shared IRQs
* than previous designs, while remaining 100% (?) SMP safe and capable.
@@ -968,7 +786,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
ide_startstop_t startstop;
int loops = 0;
- /* caller must own ide_lock */
+ /* caller must own hwgroup->lock */
BUG_ON(!irqs_disabled());
while (!hwgroup->busy) {
@@ -1023,12 +841,12 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
}
again:
hwif = HWIF(drive);
- if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif) {
+ if (hwif != hwgroup->hwif) {
/*
* set nIEN for previous hwif, drives in the
* quirk_list may not like intr setups/cleanups
*/
- if (drive->quirk_list != 1)
+ if (drive->quirk_list == 0)
hwif->tp_ops->set_irq(hwif, 0);
}
hwgroup->hwif = hwif;
@@ -1036,11 +854,6 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED);
drive->service_start = jiffies;
- if (blk_queue_plugged(drive->queue)) {
- printk(KERN_ERR "ide: huh? queue was plugged!\n");
- break;
- }
-
/*
* we know that the queue isn't empty, but this can happen
* if the q->prep_rq_fn() decides to kill a request
@@ -1090,11 +903,11 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
*/
if (masked_irq != IDE_NO_IRQ && hwif->irq != masked_irq)
disable_irq_nosync(hwif->irq);
- spin_unlock(&ide_lock);
+ spin_unlock(&hwgroup->lock);
local_irq_enable_in_hardirq();
/* allow other IRQs while we start this request */
startstop = start_request(drive, rq);
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
if (masked_irq != IDE_NO_IRQ && hwif->irq != masked_irq)
enable_irq(hwif->irq);
if (startstop == ide_stopped)
@@ -1192,7 +1005,7 @@ void ide_timer_expiry (unsigned long data)
unsigned long flags;
unsigned long wait = -1;
- spin_lock_irqsave(&ide_lock, flags);
+ spin_lock_irqsave(&hwgroup->lock, flags);
if (((handler = hwgroup->handler) == NULL) ||
(hwgroup->req_gen != hwgroup->req_gen_timer)) {
@@ -1225,7 +1038,7 @@ void ide_timer_expiry (unsigned long data)
hwgroup->timer.expires = jiffies + wait;
hwgroup->req_gen_timer = hwgroup->req_gen;
add_timer(&hwgroup->timer);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
return;
}
}
@@ -1235,7 +1048,7 @@ void ide_timer_expiry (unsigned long data)
* the handler() function, which means we need to
* globally mask the specific IRQ:
*/
- spin_unlock(&ide_lock);
+ spin_unlock(&hwgroup->lock);
hwif = HWIF(drive);
/* disable_irq_nosync ?? */
disable_irq(hwif->irq);
@@ -1259,14 +1072,14 @@ void ide_timer_expiry (unsigned long data)
hwif->tp_ops->read_status(hwif));
}
drive->service_time = jiffies - drive->service_start;
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
enable_irq(hwif->irq);
if (startstop == ide_stopped)
hwgroup->busy = 0;
}
}
ide_do_request(hwgroup, IDE_NO_IRQ);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
}
/**
@@ -1359,18 +1172,16 @@ irqreturn_t ide_intr (int irq, void *dev_id)
{
unsigned long flags;
ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id;
- ide_hwif_t *hwif;
+ ide_hwif_t *hwif = hwgroup->hwif;
ide_drive_t *drive;
ide_handler_t *handler;
ide_startstop_t startstop;
+ irqreturn_t irq_ret = IRQ_NONE;
- spin_lock_irqsave(&ide_lock, flags);
- hwif = hwgroup->hwif;
+ spin_lock_irqsave(&hwgroup->lock, flags);
- if (!ide_ack_intr(hwif)) {
- spin_unlock_irqrestore(&ide_lock, flags);
- return IRQ_NONE;
- }
+ if (!ide_ack_intr(hwif))
+ goto out;
if ((handler = hwgroup->handler) == NULL || hwgroup->polling) {
/*
@@ -1406,9 +1217,9 @@ irqreturn_t ide_intr (int irq, void *dev_id)
(void)hwif->tp_ops->read_status(hwif);
#endif /* CONFIG_BLK_DEV_IDEPCI */
}
- spin_unlock_irqrestore(&ide_lock, flags);
- return IRQ_NONE;
+ goto out;
}
+
drive = hwgroup->drive;
if (!drive) {
/*
@@ -1417,10 +1228,10 @@ irqreturn_t ide_intr (int irq, void *dev_id)
*
* [Note - this can occur if the drive is hot unplugged]
*/
- spin_unlock_irqrestore(&ide_lock, flags);
- return IRQ_HANDLED;
+ goto out_handled;
}
- if (!drive_is_ready(drive)) {
+
+ if (!drive_is_ready(drive))
/*
* This happens regularly when we share a PCI IRQ with
* another device. Unfortunately, it can also happen
@@ -1428,9 +1239,8 @@ irqreturn_t ide_intr (int irq, void *dev_id)
* their status register is up to date. Hopefully we have
* enough advance overhead that the latter isn't a problem.
*/
- spin_unlock_irqrestore(&ide_lock, flags);
- return IRQ_NONE;
- }
+ goto out;
+
if (!hwgroup->busy) {
hwgroup->busy = 1; /* paranoia */
printk(KERN_ERR "%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name);
@@ -1438,7 +1248,7 @@ irqreturn_t ide_intr (int irq, void *dev_id)
hwgroup->handler = NULL;
hwgroup->req_gen++;
del_timer(&hwgroup->timer);
- spin_unlock(&ide_lock);
+ spin_unlock(&hwgroup->lock);
if (hwif->port_ops && hwif->port_ops->clear_irq)
hwif->port_ops->clear_irq(drive);
@@ -1449,7 +1259,7 @@ irqreturn_t ide_intr (int irq, void *dev_id)
/* service this interrupt, may set handler for next interrupt */
startstop = handler(drive);
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
/*
* Note that handler() may have set things up for another
* interrupt to occur soon, but it cannot happen until
@@ -1467,8 +1277,11 @@ irqreturn_t ide_intr (int irq, void *dev_id)
"on exit\n", drive->name);
}
}
- spin_unlock_irqrestore(&ide_lock, flags);
- return IRQ_HANDLED;
+out_handled:
+ irq_ret = IRQ_HANDLED;
+out:
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
+ return irq_ret;
}
/**
@@ -1488,16 +1301,17 @@ irqreturn_t ide_intr (int irq, void *dev_id)
void ide_do_drive_cmd(ide_drive_t *drive, struct request *rq)
{
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
+ struct request_queue *q = drive->queue;
unsigned long flags;
- ide_hwgroup_t *hwgroup = HWGROUP(drive);
- spin_lock_irqsave(&ide_lock, flags);
hwgroup->rq = NULL;
- __elv_add_request(drive->queue, rq, ELEVATOR_INSERT_FRONT, 0);
- blk_start_queueing(drive->queue);
- spin_unlock_irqrestore(&ide_lock, flags);
-}
+ spin_lock_irqsave(q->queue_lock, flags);
+ __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0);
+ blk_start_queueing(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+}
EXPORT_SYMBOL(ide_do_drive_cmd);
void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma)
diff --git a/drivers/ide/ide-ioctls.c b/drivers/ide/ide-ioctls.c
index fcde16bb53a7..28232c64c346 100644
--- a/drivers/ide/ide-ioctls.c
+++ b/drivers/ide/ide-ioctls.c
@@ -19,7 +19,6 @@ int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev,
const struct ide_ioctl_devset *s)
{
const struct ide_devset *ds;
- unsigned long flags;
int err = -EOPNOTSUPP;
for (; (ds = s->setting); s++) {
@@ -33,9 +32,7 @@ int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev,
read_val:
mutex_lock(&ide_setting_mtx);
- spin_lock_irqsave(&ide_lock, flags);
err = ds->get(drive);
- spin_unlock_irqrestore(&ide_lock, flags);
mutex_unlock(&ide_setting_mtx);
return err >= 0 ? put_user(err, (long __user *)arg) : err;
@@ -98,7 +95,7 @@ static int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg)
return -EPERM;
if (((arg >> IDE_NICE_DSC_OVERLAP) & 1) &&
- (drive->media == ide_disk || drive->media == ide_floppy ||
+ (drive->media != ide_tape ||
(drive->dev_flags & IDE_DFLAG_SCSI)))
return -EPERM;
diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
index c41c3b9b6f02..ad8bd6539283 100644
--- a/drivers/ide/ide-iops.c
+++ b/drivers/ide/ide-iops.c
@@ -835,10 +835,12 @@ static void __ide_set_handler (ide_drive_t *drive, ide_handler_t *handler,
void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler,
unsigned int timeout, ide_expiry_t *expiry)
{
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
unsigned long flags;
- spin_lock_irqsave(&ide_lock, flags);
+
+ spin_lock_irqsave(&hwgroup->lock, flags);
__ide_set_handler(drive, handler, timeout, expiry);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
}
EXPORT_SYMBOL(ide_set_handler);
@@ -860,10 +862,11 @@ EXPORT_SYMBOL(ide_set_handler);
void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler,
unsigned timeout, ide_expiry_t *expiry)
{
+ ide_hwif_t *hwif = drive->hwif;
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
unsigned long flags;
- ide_hwif_t *hwif = HWIF(drive);
- spin_lock_irqsave(&ide_lock, flags);
+ spin_lock_irqsave(&hwgroup->lock, flags);
__ide_set_handler(drive, handler, timeout, expiry);
hwif->tp_ops->exec_command(hwif, cmd);
/*
@@ -873,19 +876,20 @@ void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler,
* FIXME: we could skip this delay with care on non shared devices
*/
ndelay(400);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
}
EXPORT_SYMBOL(ide_execute_command);
void ide_execute_pkt_cmd(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
unsigned long flags;
- spin_lock_irqsave(&ide_lock, flags);
+ spin_lock_irqsave(&hwgroup->lock, flags);
hwif->tp_ops->exec_command(hwif, ATA_CMD_PACKET);
ndelay(400);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
}
EXPORT_SYMBOL_GPL(ide_execute_pkt_cmd);
@@ -1076,22 +1080,16 @@ static void pre_reset(ide_drive_t *drive)
*/
static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
{
- unsigned int unit;
- unsigned long flags, timeout;
- ide_hwif_t *hwif;
- ide_hwgroup_t *hwgroup;
- struct ide_io_ports *io_ports;
- const struct ide_tp_ops *tp_ops;
+ ide_hwif_t *hwif = drive->hwif;
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ const struct ide_tp_ops *tp_ops = hwif->tp_ops;
const struct ide_port_ops *port_ops;
+ unsigned long flags, timeout;
+ unsigned int unit;
DEFINE_WAIT(wait);
- spin_lock_irqsave(&ide_lock, flags);
- hwif = HWIF(drive);
- hwgroup = HWGROUP(drive);
-
- io_ports = &hwif->io_ports;
-
- tp_ops = hwif->tp_ops;
+ spin_lock_irqsave(&hwgroup->lock, flags);
/* We must not reset with running handlers */
BUG_ON(hwgroup->handler != NULL);
@@ -1106,7 +1104,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
hwgroup->polling = 1;
__ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20, NULL);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
return ide_started;
}
@@ -1129,9 +1127,9 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
if (time_before_eq(timeout, now))
break;
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
timeout = schedule_timeout_uninterruptible(timeout - now);
- spin_lock_irqsave(&ide_lock, flags);
+ spin_lock_irqsave(&hwgroup->lock, flags);
} while (timeout);
finish_wait(&ide_park_wq, &wait);
@@ -1143,7 +1141,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
pre_reset(&hwif->drives[unit]);
if (io_ports->ctl_addr == 0) {
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
ide_complete_drive_reset(drive, -ENXIO);
return ide_stopped;
}
@@ -1179,7 +1177,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
if (port_ops && port_ops->resetproc)
port_ops->resetproc(drive);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
return ide_started;
}
diff --git a/drivers/ide/ide-legacy.c b/drivers/ide/ide-legacy.c
new file mode 100644
index 000000000000..8c5dcbf22547
--- /dev/null
+++ b/drivers/ide/ide-legacy.c
@@ -0,0 +1,58 @@
+#include <linux/kernel.h>
+#include <linux/ide.h>
+
+static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw,
+ u8 port_no, const struct ide_port_info *d,
+ unsigned long config)
+{
+ unsigned long base, ctl;
+ int irq;
+
+ if (port_no == 0) {
+ base = 0x1f0;
+ ctl = 0x3f6;
+ irq = 14;
+ } else {
+ base = 0x170;
+ ctl = 0x376;
+ irq = 15;
+ }
+
+ if (!request_region(base, 8, d->name)) {
+ printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n",
+ d->name, base, base + 7);
+ return;
+ }
+
+ if (!request_region(ctl, 1, d->name)) {
+ printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n",
+ d->name, ctl);
+ release_region(base, 8);
+ return;
+ }
+
+ ide_std_init_ports(hw, base, ctl);
+ hw->irq = irq;
+ hw->chipset = d->chipset;
+ hw->config = config;
+
+ hws[port_no] = hw;
+}
+
+int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config)
+{
+ hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL };
+
+ memset(&hw, 0, sizeof(hw));
+
+ if ((d->host_flags & IDE_HFLAG_QD_2ND_PORT) == 0)
+ ide_legacy_init_one(hws, &hw[0], 0, d, config);
+ ide_legacy_init_one(hws, &hw[1], 1, d, config);
+
+ if (hws[0] == NULL && hws[1] == NULL &&
+ (d->host_flags & IDE_HFLAG_SINGLE))
+ return -ENOENT;
+
+ return ide_host_add(d, hws, NULL);
+}
+EXPORT_SYMBOL_GPL(ide_legacy_device_add);
diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c
index 9fc4cfb2a272..9f6e33d8a8b2 100644
--- a/drivers/ide/ide-lib.c
+++ b/drivers/ide/ide-lib.c
@@ -43,7 +43,6 @@ const char *ide_xfer_verbose(u8 mode)
return s;
}
-
EXPORT_SYMBOL(ide_xfer_verbose);
/**
@@ -87,7 +86,7 @@ static u8 ide_rate_filter(ide_drive_t *drive, u8 speed)
* This is used by most chipset support modules when "auto-tuning".
*/
-u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
+u8 ide_get_best_pio_mode(ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
{
u16 *id = drive->id;
int pio_mode = -1, overridden = 0;
@@ -131,7 +130,6 @@ u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
return pio_mode;
}
-
EXPORT_SYMBOL_GPL(ide_get_best_pio_mode);
/* req_pio == "255" for auto-tune */
@@ -162,7 +160,6 @@ void ide_set_pio(ide_drive_t *drive, u8 req_pio)
(void)ide_set_pio_mode(drive, XFER_PIO_0 + pio);
}
-
EXPORT_SYMBOL_GPL(ide_set_pio);
/**
@@ -173,7 +170,7 @@ EXPORT_SYMBOL_GPL(ide_set_pio);
* Enable or disable bounce buffering for the device. Drives move
* between PIO and DMA and that changes the rules we need.
*/
-
+
void ide_toggle_bounce(ide_drive_t *drive, int on)
{
u64 addr = BLK_BOUNCE_HIGH; /* dma64_addr_t */
@@ -243,14 +240,13 @@ int ide_set_dma_mode(ide_drive_t *drive, const u8 mode)
return ide_config_drive_speed(drive, mode);
}
}
-
EXPORT_SYMBOL_GPL(ide_set_dma_mode);
/**
* ide_set_xfer_rate - set transfer rate
* @drive: drive to set
* @rate: speed to attempt to set
- *
+ *
* General helper for setting the speed of an IDE device. This
* function knows about user enforced limits from the configuration
* which ->set_pio_mode/->set_dma_mode does not.
@@ -277,21 +273,16 @@ int ide_set_xfer_rate(ide_drive_t *drive, u8 rate)
static void ide_dump_opcode(ide_drive_t *drive)
{
- struct request *rq;
+ struct request *rq = drive->hwif->hwgroup->rq;
ide_task_t *task = NULL;
- spin_lock(&ide_lock);
- rq = NULL;
- if (HWGROUP(drive))
- rq = HWGROUP(drive)->rq;
- spin_unlock(&ide_lock);
if (!rq)
return;
if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE)
task = rq->special;
- printk("ide: failed opcode was: ");
+ printk(KERN_ERR "ide: failed opcode was: ");
if (task == NULL)
printk(KERN_CONT "unknown\n");
else
@@ -329,44 +320,55 @@ static void ide_dump_sector(ide_drive_t *drive)
drive->hwif->tp_ops->tf_read(drive, &task);
if (lba48 || (tf->device & ATA_LBA))
- printk(", LBAsect=%llu",
+ printk(KERN_CONT ", LBAsect=%llu",
(unsigned long long)ide_get_lba_addr(tf, lba48));
else
- printk(", CHS=%d/%d/%d", (tf->lbah << 8) + tf->lbam,
- tf->device & 0xf, tf->lbal);
+ printk(KERN_CONT ", CHS=%d/%d/%d", (tf->lbah << 8) + tf->lbam,
+ tf->device & 0xf, tf->lbal);
}
static void ide_dump_ata_error(ide_drive_t *drive, u8 err)
{
- printk("{ ");
- if (err & ATA_ABORTED) printk("DriveStatusError ");
+ printk(KERN_ERR "{ ");
+ if (err & ATA_ABORTED)
+ printk(KERN_CONT "DriveStatusError ");
if (err & ATA_ICRC)
- printk((err & ATA_ABORTED) ? "BadCRC " : "BadSector ");
- if (err & ATA_UNC) printk("UncorrectableError ");
- if (err & ATA_IDNF) printk("SectorIdNotFound ");
- if (err & ATA_TRK0NF) printk("TrackZeroNotFound ");
- if (err & ATA_AMNF) printk("AddrMarkNotFound ");
- printk("}");
+ printk(KERN_CONT "%s",
+ (err & ATA_ABORTED) ? "BadCRC " : "BadSector ");
+ if (err & ATA_UNC)
+ printk(KERN_CONT "UncorrectableError ");
+ if (err & ATA_IDNF)
+ printk(KERN_CONT "SectorIdNotFound ");
+ if (err & ATA_TRK0NF)
+ printk(KERN_CONT "TrackZeroNotFound ");
+ if (err & ATA_AMNF)
+ printk(KERN_CONT "AddrMarkNotFound ");
+ printk(KERN_CONT "}");
if ((err & (ATA_BBK | ATA_ABORTED)) == ATA_BBK ||
(err & (ATA_UNC | ATA_IDNF | ATA_AMNF))) {
ide_dump_sector(drive);
if (HWGROUP(drive) && HWGROUP(drive)->rq)
- printk(", sector=%llu",
+ printk(KERN_CONT ", sector=%llu",
(unsigned long long)HWGROUP(drive)->rq->sector);
}
- printk("\n");
+ printk(KERN_CONT "\n");
}
static void ide_dump_atapi_error(ide_drive_t *drive, u8 err)
{
- printk("{ ");
- if (err & ATAPI_ILI) printk("IllegalLengthIndication ");
- if (err & ATAPI_EOM) printk("EndOfMedia ");
- if (err & ATA_ABORTED) printk("AbortedCommand ");
- if (err & ATA_MCR) printk("MediaChangeRequested ");
- if (err & ATAPI_LFS) printk("LastFailedSense=0x%02x ",
- (err & ATAPI_LFS) >> 4);
- printk("}\n");
+ printk(KERN_ERR "{ ");
+ if (err & ATAPI_ILI)
+ printk(KERN_CONT "IllegalLengthIndication ");
+ if (err & ATAPI_EOM)
+ printk(KERN_CONT "EndOfMedia ");
+ if (err & ATA_ABORTED)
+ printk(KERN_CONT "AbortedCommand ");
+ if (err & ATA_MCR)
+ printk(KERN_CONT "MediaChangeRequested ");
+ if (err & ATAPI_LFS)
+ printk(KERN_CONT "LastFailedSense=0x%02x ",
+ (err & ATAPI_LFS) >> 4);
+ printk(KERN_CONT "}\n");
}
/**
@@ -382,34 +384,37 @@ static void ide_dump_atapi_error(ide_drive_t *drive, u8 err)
u8 ide_dump_status(ide_drive_t *drive, const char *msg, u8 stat)
{
- unsigned long flags;
u8 err = 0;
- local_irq_save(flags);
- printk("%s: %s: status=0x%02x { ", drive->name, msg, stat);
+ printk(KERN_ERR "%s: %s: status=0x%02x { ", drive->name, msg, stat);
if (stat & ATA_BUSY)
- printk("Busy ");
+ printk(KERN_CONT "Busy ");
else {
- if (stat & ATA_DRDY) printk("DriveReady ");
- if (stat & ATA_DF) printk("DeviceFault ");
- if (stat & ATA_DSC) printk("SeekComplete ");
- if (stat & ATA_DRQ) printk("DataRequest ");
- if (stat & ATA_CORR) printk("CorrectedError ");
- if (stat & ATA_IDX) printk("Index ");
- if (stat & ATA_ERR) printk("Error ");
+ if (stat & ATA_DRDY)
+ printk(KERN_CONT "DriveReady ");
+ if (stat & ATA_DF)
+ printk(KERN_CONT "DeviceFault ");
+ if (stat & ATA_DSC)
+ printk(KERN_CONT "SeekComplete ");
+ if (stat & ATA_DRQ)
+ printk(KERN_CONT "DataRequest ");
+ if (stat & ATA_CORR)
+ printk(KERN_CONT "CorrectedError ");
+ if (stat & ATA_IDX)
+ printk(KERN_CONT "Index ");
+ if (stat & ATA_ERR)
+ printk(KERN_CONT "Error ");
}
- printk("}\n");
+ printk(KERN_CONT "}\n");
if ((stat & (ATA_BUSY | ATA_ERR)) == ATA_ERR) {
err = ide_read_error(drive);
- printk("%s: %s: error=0x%02x ", drive->name, msg, err);
+ printk(KERN_ERR "%s: %s: error=0x%02x ", drive->name, msg, err);
if (drive->media == ide_disk)
ide_dump_ata_error(drive, err);
else
ide_dump_atapi_error(drive, err);
}
ide_dump_opcode(drive);
- local_irq_restore(flags);
return err;
}
-
EXPORT_SYMBOL(ide_dump_status);
diff --git a/drivers/ide/ide-park.c b/drivers/ide/ide-park.c
index 03b00e57e93f..63d01c55f865 100644
--- a/drivers/ide/ide-park.c
+++ b/drivers/ide/ide-park.c
@@ -7,17 +7,16 @@ DECLARE_WAIT_QUEUE_HEAD(ide_park_wq);
static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout)
{
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
struct request_queue *q = drive->queue;
struct request *rq;
int rc;
timeout += jiffies;
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
if (drive->dev_flags & IDE_DFLAG_PARKED) {
- ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
- int reset_timer;
+ int reset_timer = time_before(timeout, drive->sleep);
- reset_timer = time_before(timeout, drive->sleep);
drive->sleep = timeout;
wake_up_all(&ide_park_wq);
if (reset_timer && hwgroup->sleeping &&
@@ -26,10 +25,10 @@ static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout)
hwgroup->busy = 0;
blk_start_queueing(q);
}
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
return;
}
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
rq = blk_get_request(q, READ, __GFP_WAIT);
rq->cmd[0] = REQ_PARK_HEADS;
@@ -62,20 +61,21 @@ ssize_t ide_park_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ide_drive_t *drive = to_ide_device(dev);
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
unsigned long now;
unsigned int msecs;
if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD)
return -EOPNOTSUPP;
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
now = jiffies;
if (drive->dev_flags & IDE_DFLAG_PARKED &&
time_after(drive->sleep, now))
msecs = jiffies_to_msecs(drive->sleep - now);
else
msecs = 0;
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
return snprintf(buf, 20, "%u\n", msecs);
}
diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c
new file mode 100644
index 000000000000..8282c6086e6a
--- /dev/null
+++ b/drivers/ide/ide-pm.c
@@ -0,0 +1,235 @@
+#include <linux/kernel.h>
+#include <linux/ide.h>
+#include <linux/hdreg.h>
+
+int generic_ide_suspend(struct device *dev, pm_message_t mesg)
+{
+ ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive);
+ ide_hwif_t *hwif = HWIF(drive);
+ struct request *rq;
+ struct request_pm_state rqpm;
+ ide_task_t args;
+ int ret;
+
+ /* call ACPI _GTM only once */
+ if ((drive->dn & 1) == 0 || pair == NULL)
+ ide_acpi_get_timing(hwif);
+
+ memset(&rqpm, 0, sizeof(rqpm));
+ memset(&args, 0, sizeof(args));
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_PM_SUSPEND;
+ rq->special = &args;
+ rq->data = &rqpm;
+ rqpm.pm_step = IDE_PM_START_SUSPEND;
+ if (mesg.event == PM_EVENT_PRETHAW)
+ mesg.event = PM_EVENT_FREEZE;
+ rqpm.pm_state = mesg.event;
+
+ ret = blk_execute_rq(drive->queue, NULL, rq, 0);
+ blk_put_request(rq);
+
+ /* call ACPI _PS3 only after both devices are suspended */
+ if (ret == 0 && ((drive->dn & 1) || pair == NULL))
+ ide_acpi_set_state(hwif, 0);
+
+ return ret;
+}
+
+int generic_ide_resume(struct device *dev)
+{
+ ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive);
+ ide_hwif_t *hwif = HWIF(drive);
+ struct request *rq;
+ struct request_pm_state rqpm;
+ ide_task_t args;
+ int err;
+
+ /* call ACPI _PS0 / _STM only once */
+ if ((drive->dn & 1) == 0 || pair == NULL) {
+ ide_acpi_set_state(hwif, 1);
+ ide_acpi_push_timing(hwif);
+ }
+
+ ide_acpi_exec_tfs(drive);
+
+ memset(&rqpm, 0, sizeof(rqpm));
+ memset(&args, 0, sizeof(args));
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_PM_RESUME;
+ rq->cmd_flags |= REQ_PREEMPT;
+ rq->special = &args;
+ rq->data = &rqpm;
+ rqpm.pm_step = IDE_PM_START_RESUME;
+ rqpm.pm_state = PM_EVENT_ON;
+
+ err = blk_execute_rq(drive->queue, NULL, rq, 1);
+ blk_put_request(rq);
+
+ if (err == 0 && dev->driver) {
+ ide_driver_t *drv = to_ide_driver(dev->driver);
+
+ if (drv->resume)
+ drv->resume(drive);
+ }
+
+ return err;
+}
+
+void ide_complete_power_step(ide_drive_t *drive, struct request *rq)
+{
+ struct request_pm_state *pm = rq->data;
+
+#ifdef DEBUG_PM
+ printk(KERN_INFO "%s: complete_power_step(step: %d)\n",
+ drive->name, pm->pm_step);
+#endif
+ if (drive->media != ide_disk)
+ return;
+
+ switch (pm->pm_step) {
+ case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */
+ if (pm->pm_state == PM_EVENT_FREEZE)
+ pm->pm_step = IDE_PM_COMPLETED;
+ else
+ pm->pm_step = IDE_PM_STANDBY;
+ break;
+ case IDE_PM_STANDBY: /* Suspend step 2 (standby) */
+ pm->pm_step = IDE_PM_COMPLETED;
+ break;
+ case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */
+ pm->pm_step = IDE_PM_IDLE;
+ break;
+ case IDE_PM_IDLE: /* Resume step 2 (idle)*/
+ pm->pm_step = IDE_PM_RESTORE_DMA;
+ break;
+ }
+}
+
+ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq)
+{
+ struct request_pm_state *pm = rq->data;
+ ide_task_t *args = rq->special;
+
+ memset(args, 0, sizeof(*args));
+
+ switch (pm->pm_step) {
+ case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */
+ if (drive->media != ide_disk)
+ break;
+ /* Not supported? Switch to next step now. */
+ if (ata_id_flush_enabled(drive->id) == 0 ||
+ (drive->dev_flags & IDE_DFLAG_WCACHE) == 0) {
+ ide_complete_power_step(drive, rq);
+ return ide_stopped;
+ }
+ if (ata_id_flush_ext_enabled(drive->id))
+ args->tf.command = ATA_CMD_FLUSH_EXT;
+ else
+ args->tf.command = ATA_CMD_FLUSH;
+ goto out_do_tf;
+ case IDE_PM_STANDBY: /* Suspend step 2 (standby) */
+ args->tf.command = ATA_CMD_STANDBYNOW1;
+ goto out_do_tf;
+ case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */
+ ide_set_max_pio(drive);
+ /*
+ * skip IDE_PM_IDLE for ATAPI devices
+ */
+ if (drive->media != ide_disk)
+ pm->pm_step = IDE_PM_RESTORE_DMA;
+ else
+ ide_complete_power_step(drive, rq);
+ return ide_stopped;
+ case IDE_PM_IDLE: /* Resume step 2 (idle) */
+ args->tf.command = ATA_CMD_IDLEIMMEDIATE;
+ goto out_do_tf;
+ case IDE_PM_RESTORE_DMA: /* Resume step 3 (restore DMA) */
+ /*
+ * Right now, all we do is call ide_set_dma(drive),
+ * we could be smarter and check for current xfer_speed
+ * in struct drive etc...
+ */
+ if (drive->hwif->dma_ops == NULL)
+ break;
+ /*
+ * TODO: respect IDE_DFLAG_USING_DMA
+ */
+ ide_set_dma(drive);
+ break;
+ }
+
+ pm->pm_step = IDE_PM_COMPLETED;
+ return ide_stopped;
+
+out_do_tf:
+ args->tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
+ args->data_phase = TASKFILE_NO_DATA;
+ return do_rw_taskfile(drive, args);
+}
+
+/**
+ * ide_complete_pm_request - end the current Power Management request
+ * @drive: target drive
+ * @rq: request
+ *
+ * This function cleans up the current PM request and stops the queue
+ * if necessary.
+ */
+void ide_complete_pm_request(ide_drive_t *drive, struct request *rq)
+{
+ struct request_queue *q = drive->queue;
+ unsigned long flags;
+
+#ifdef DEBUG_PM
+ printk("%s: completing PM request, %s\n", drive->name,
+ blk_pm_suspend_request(rq) ? "suspend" : "resume");
+#endif
+ spin_lock_irqsave(q->queue_lock, flags);
+ if (blk_pm_suspend_request(rq)) {
+ blk_stop_queue(q);
+ } else {
+ drive->dev_flags &= ~IDE_DFLAG_BLOCKED;
+ blk_start_queue(q);
+ }
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ drive->hwif->hwgroup->rq = NULL;
+
+ if (blk_end_request(rq, 0, 0))
+ BUG();
+}
+
+void ide_check_pm_state(ide_drive_t *drive, struct request *rq)
+{
+ struct request_pm_state *pm = rq->data;
+
+ if (blk_pm_suspend_request(rq) &&
+ pm->pm_step == IDE_PM_START_SUSPEND)
+ /* Mark drive blocked when starting the suspend sequence. */
+ drive->dev_flags |= IDE_DFLAG_BLOCKED;
+ else if (blk_pm_resume_request(rq) &&
+ pm->pm_step == IDE_PM_START_RESUME) {
+ /*
+ * The first thing we do on wakeup is to wait for BSY bit to
+ * go away (with a looong timeout) as a drive on this hwif may
+ * just be POSTing itself.
+ * We do that before even selecting as the "other" device on
+ * the bus may be broken enough to walk on our toes at this
+ * point.
+ */
+ ide_hwif_t *hwif = drive->hwif;
+ int rc;
+#ifdef DEBUG_PM
+ printk("%s: Wakeup request inited, waiting for !BSY...\n", drive->name);
+#endif
+ rc = ide_wait_not_busy(hwif, 35000);
+ if (rc)
+ printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name);
+ SELECT_DRIVE(drive);
+ hwif->tp_ops->set_irq(hwif, 1);
+ rc = ide_wait_not_busy(hwif, 100000);
+ if (rc)
+ printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name);
+ }
+}
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index c55bdbd22314..a64ec259f3d1 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -110,20 +110,22 @@ static void ide_disk_init_mult_count(ide_drive_t *drive)
* read and parse the results. This function is run with
* interrupts disabled.
*/
-
-static inline void do_identify (ide_drive_t *drive, u8 cmd)
+
+static void do_identify(ide_drive_t *drive, u8 cmd)
{
ide_hwif_t *hwif = HWIF(drive);
u16 *id = drive->id;
char *m = (char *)&id[ATA_ID_PROD];
+ unsigned long flags;
int bswap = 1, is_cfa;
+ /* local CPU only; some systems need this */
+ local_irq_save(flags);
/* read 512 bytes of id info */
hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE);
+ local_irq_restore(flags);
drive->dev_flags |= IDE_DFLAG_ID_READ;
-
- local_irq_enable();
#ifdef DEBUG
printk(KERN_INFO "%s: dumping identify data\n", drive->name);
ide_dump_identify((u8 *)id);
@@ -306,17 +308,12 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd)
s = tp_ops->read_status(hwif);
if (OK_STAT(s, ATA_DRQ, BAD_R_STAT)) {
- unsigned long flags;
-
- /* local CPU only; some systems need this */
- local_irq_save(flags);
/* drive returned ID */
do_identify(drive, cmd);
/* drive responded with ID */
rc = 0;
/* clear drive IRQ */
(void)tp_ops->read_status(hwif);
- local_irq_restore(flags);
} else {
/* drive refused ID */
rc = 2;
@@ -554,8 +551,8 @@ static void enable_nest (ide_drive_t *drive)
* 1 device was found
* (note: IDE_DFLAG_PRESENT might still be not set)
*/
-
-static inline u8 probe_for_drive (ide_drive_t *drive)
+
+static u8 probe_for_drive(ide_drive_t *drive)
{
char *m;
@@ -642,7 +639,7 @@ static int ide_register_port(ide_hwif_t *hwif)
int ret;
/* register with global device tree */
- strlcpy(hwif->gendev.bus_id,hwif->name,BUS_ID_SIZE);
+ dev_set_name(&hwif->gendev, hwif->name);
hwif->gendev.driver_data = hwif;
if (hwif->gendev.parent == NULL) {
if (hwif->dev)
@@ -864,31 +861,6 @@ static void ide_port_tune_devices(ide_hwif_t *hwif)
}
/*
- * save_match() is used to simplify logic in init_irq() below.
- *
- * A loophole here is that we may not know about a particular
- * hwif's irq until after that hwif is actually probed/initialized..
- * This could be a problem for the case where an hwif is on a
- * dual interface that requires serialization (eg. cmd640) and another
- * hwif using one of the same irqs is initialized beforehand.
- *
- * This routine detects and reports such situations, but does not fix them.
- */
-static void save_match(ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match)
-{
- ide_hwif_t *m = *match;
-
- if (m && m->hwgroup && m->hwgroup != new->hwgroup) {
- if (!new->hwgroup)
- return;
- printk(KERN_WARNING "%s: potential IRQ problem with %s and %s\n",
- hwif->name, new->name, m->name);
- }
- if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */
- *match = new;
-}
-
-/*
* init request queue
*/
static int ide_init_queue(ide_drive_t *drive)
@@ -906,7 +878,8 @@ static int ide_init_queue(ide_drive_t *drive)
* do not.
*/
- q = blk_init_queue_node(do_ide_request, &ide_lock, hwif_to_node(hwif));
+ q = blk_init_queue_node(do_ide_request, &hwif->hwgroup->lock,
+ hwif_to_node(hwif));
if (!q)
return 1;
@@ -947,7 +920,7 @@ static void ide_add_drive_to_hwgroup(ide_drive_t *drive)
{
ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
if (!hwgroup->drive) {
/* first drive for hwgroup. */
drive->next = drive;
@@ -957,7 +930,7 @@ static void ide_add_drive_to_hwgroup(ide_drive_t *drive)
drive->next = hwgroup->drive->next;
hwgroup->drive->next = drive;
}
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
}
/*
@@ -1002,7 +975,7 @@ void ide_remove_port_from_hwgroup(ide_hwif_t *hwif)
ide_ports[hwif->index] = NULL;
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
/*
* Remove us from the hwgroup, and free
* the hwgroup if we were the only member
@@ -1030,7 +1003,7 @@ void ide_remove_port_from_hwgroup(ide_hwif_t *hwif)
}
BUG_ON(hwgroup->hwif == hwif);
}
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
}
/*
@@ -1051,27 +1024,13 @@ static int init_irq (ide_hwif_t *hwif)
mutex_lock(&ide_cfg_mtx);
hwif->hwgroup = NULL;
- /*
- * Group up with any other hwifs that share our irq(s).
- */
for (index = 0; index < MAX_HWIFS; index++) {
ide_hwif_t *h = ide_ports[index];
if (h && h->hwgroup) { /* scan only initialized ports */
- if (hwif->irq == h->irq) {
- hwif->sharing_irq = h->sharing_irq = 1;
- if (hwif->chipset != ide_pci ||
- h->chipset != ide_pci) {
- save_match(hwif, h, &match);
- }
- }
- if (hwif->serialized) {
- if (hwif->mate && hwif->mate->irq == h->irq)
- save_match(hwif, h, &match);
- }
- if (h->serialized) {
- if (h->mate && hwif->irq == h->mate->irq)
- save_match(hwif, h, &match);
+ if (hwif->host->host_flags & IDE_HFLAG_SERIALIZE) {
+ if (hwif->host == h->host)
+ match = h;
}
}
}
@@ -1092,17 +1051,19 @@ static int init_irq (ide_hwif_t *hwif)
* linked list, the first entry is the hwif that owns
* hwgroup->handler - do not change that.
*/
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
hwif->next = hwgroup->hwif->next;
hwgroup->hwif->next = hwif;
BUG_ON(hwif->next == hwif);
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
} else {
hwgroup = kmalloc_node(sizeof(*hwgroup), GFP_KERNEL|__GFP_ZERO,
hwif_to_node(hwif));
if (hwgroup == NULL)
goto out_up;
+ spin_lock_init(&hwgroup->lock);
+
hwif->hwgroup = hwgroup;
hwgroup->hwif = hwif->next = hwif;
@@ -1122,8 +1083,7 @@ static int init_irq (ide_hwif_t *hwif)
sa = IRQF_SHARED;
#endif /* __mc68000__ */
- if (hwif->chipset == ide_pci || hwif->chipset == ide_cmd646 ||
- hwif->chipset == ide_ali14xx)
+ if (hwif->chipset == ide_pci)
sa = IRQF_SHARED;
if (io_ports->ctl_addr)
@@ -1150,8 +1110,7 @@ static int init_irq (ide_hwif_t *hwif)
io_ports->data_addr, hwif->irq);
#endif /* __mc68000__ */
if (match)
- printk(KERN_CONT " (%sed with %s)",
- hwif->sharing_irq ? "shar" : "serializ", match->name);
+ printk(KERN_CONT " (serialized with %s)", match->name);
printk(KERN_CONT "\n");
mutex_unlock(&ide_cfg_mtx);
@@ -1263,20 +1222,21 @@ static void ide_remove_drive_from_hwgroup(ide_drive_t *drive)
static void drive_release_dev (struct device *dev)
{
ide_drive_t *drive = container_of(dev, ide_drive_t, gendev);
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
ide_proc_unregister_device(drive);
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
ide_remove_drive_from_hwgroup(drive);
kfree(drive->id);
drive->id = NULL;
drive->dev_flags &= ~IDE_DFLAG_PRESENT;
/* Messed up locking ... */
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
blk_cleanup_queue(drive->queue);
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
drive->queue = NULL;
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
complete(&drive->gendev_rel_comp);
}
@@ -1352,7 +1312,7 @@ static void hwif_register_devices(ide_hwif_t *hwif)
if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
continue;
- snprintf(dev->bus_id, BUS_ID_SIZE, "%u.%u", hwif->index, i);
+ dev_set_name(dev, "%u.%u", hwif->index, i);
dev->parent = &hwif->gendev;
dev->bus = &ide_bus_type;
dev->driver_data = drive;
@@ -1436,13 +1396,11 @@ static void ide_init_port(ide_hwif_t *hwif, unsigned int port,
}
if ((d->host_flags & IDE_HFLAG_SERIALIZE) ||
- ((d->host_flags & IDE_HFLAG_SERIALIZE_DMA) && hwif->dma_base)) {
- if (hwif->mate)
- hwif->mate->serialized = hwif->serialized = 1;
- }
+ ((d->host_flags & IDE_HFLAG_SERIALIZE_DMA) && hwif->dma_base))
+ hwif->host->host_flags |= IDE_HFLAG_SERIALIZE;
- if (d->host_flags & IDE_HFLAG_RQSIZE_256)
- hwif->rqsize = 256;
+ if (d->max_sectors)
+ hwif->rqsize = d->max_sectors;
/* call chipset specific routine for each enabled port */
if (d->init_hwif)
@@ -1794,59 +1752,3 @@ void ide_port_scan(ide_hwif_t *hwif)
ide_proc_port_register_devices(hwif);
}
EXPORT_SYMBOL_GPL(ide_port_scan);
-
-static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw,
- u8 port_no, const struct ide_port_info *d,
- unsigned long config)
-{
- unsigned long base, ctl;
- int irq;
-
- if (port_no == 0) {
- base = 0x1f0;
- ctl = 0x3f6;
- irq = 14;
- } else {
- base = 0x170;
- ctl = 0x376;
- irq = 15;
- }
-
- if (!request_region(base, 8, d->name)) {
- printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n",
- d->name, base, base + 7);
- return;
- }
-
- if (!request_region(ctl, 1, d->name)) {
- printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n",
- d->name, ctl);
- release_region(base, 8);
- return;
- }
-
- ide_std_init_ports(hw, base, ctl);
- hw->irq = irq;
- hw->chipset = d->chipset;
- hw->config = config;
-
- hws[port_no] = hw;
-}
-
-int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config)
-{
- hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL };
-
- memset(&hw, 0, sizeof(hw));
-
- if ((d->host_flags & IDE_HFLAG_QD_2ND_PORT) == 0)
- ide_legacy_init_one(hws, &hw[0], 0, d, config);
- ide_legacy_init_one(hws, &hw[1], 1, d, config);
-
- if (hws[0] == NULL && hws[1] == NULL &&
- (d->host_flags & IDE_HFLAG_SINGLE))
- return -ENOENT;
-
- return ide_host_add(d, hws, NULL);
-}
-EXPORT_SYMBOL_GPL(ide_legacy_device_add);
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index f3cddd1b2f8f..a14e2938e4f3 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -46,10 +46,6 @@ static int proc_ide_read_imodel
case ide_qd65xx: name = "qd65xx"; break;
case ide_umc8672: name = "umc8672"; break;
case ide_ht6560b: name = "ht6560b"; break;
- case ide_rz1000: name = "rz1000"; break;
- case ide_trm290: name = "trm290"; break;
- case ide_cmd646: name = "cmd646"; break;
- case ide_cy82c693: name = "cy82c693"; break;
case ide_4drives: name = "4drives"; break;
case ide_pmac: name = "mac-io"; break;
case ide_au1xxx: name = "au1xxx"; break;
@@ -155,13 +151,8 @@ static int ide_read_setting(ide_drive_t *drive,
const struct ide_devset *ds = setting->setting;
int val = -EINVAL;
- if (ds->get) {
- unsigned long flags;
-
- spin_lock_irqsave(&ide_lock, flags);
+ if (ds->get)
val = ds->get(drive);
- spin_unlock_irqrestore(&ide_lock, flags);
- }
return val;
}
@@ -583,31 +574,19 @@ EXPORT_SYMBOL(ide_proc_register_driver);
* Clean up the driver specific /proc files and IDE settings
* for a given drive.
*
- * Takes ide_setting_mtx and ide_lock.
- * Caller must hold none of the locks.
+ * Takes ide_setting_mtx.
*/
void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver)
{
- unsigned long flags;
-
ide_remove_proc_entries(drive->proc, driver->proc_entries(drive));
mutex_lock(&ide_setting_mtx);
- spin_lock_irqsave(&ide_lock, flags);
/*
- * ide_setting_mtx protects the settings list
- * ide_lock protects the use of settings
- *
- * so we need to hold both, ide_settings_sem because we want to
- * modify the settings list, and ide_lock because we cannot take
- * a setting out that is being used.
- *
- * OTOH both ide_{read,write}_setting are only ever used under
- * ide_setting_mtx.
+ * ide_setting_mtx protects both the settings list and the use
+ * of settings (we cannot take a setting out that is being used).
*/
drive->settings = NULL;
- spin_unlock_irqrestore(&ide_lock, flags);
mutex_unlock(&ide_setting_mtx);
}
EXPORT_SYMBOL(ide_proc_unregister_driver);
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index 04f8f13cb9d7..f0f09f702e9c 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -74,9 +74,6 @@ static const u8 ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR,
DEFINE_MUTEX(ide_cfg_mtx);
-__cacheline_aligned_in_smp DEFINE_SPINLOCK(ide_lock);
-EXPORT_SYMBOL(ide_lock);
-
static void ide_port_init_devices_data(ide_hwif_t *);
/*
@@ -130,7 +127,6 @@ static void ide_port_init_devices_data(ide_hwif_t *hwif)
}
}
-/* Called with ide_lock held. */
static void __ide_port_unregister_devices(ide_hwif_t *hwif)
{
int i;
@@ -139,10 +135,8 @@ static void __ide_port_unregister_devices(ide_hwif_t *hwif)
ide_drive_t *drive = &hwif->drives[i];
if (drive->dev_flags & IDE_DFLAG_PRESENT) {
- spin_unlock_irq(&ide_lock);
device_unregister(&drive->gendev);
wait_for_completion(&drive->gendev_rel_comp);
- spin_lock_irq(&ide_lock);
}
}
}
@@ -150,11 +144,9 @@ static void __ide_port_unregister_devices(ide_hwif_t *hwif)
void ide_port_unregister_devices(ide_hwif_t *hwif)
{
mutex_lock(&ide_cfg_mtx);
- spin_lock_irq(&ide_lock);
__ide_port_unregister_devices(hwif);
hwif->present = 0;
ide_port_init_devices_data(hwif);
- spin_unlock_irq(&ide_lock);
mutex_unlock(&ide_cfg_mtx);
}
EXPORT_SYMBOL_GPL(ide_port_unregister_devices);
@@ -192,12 +184,10 @@ void ide_unregister(ide_hwif_t *hwif)
mutex_lock(&ide_cfg_mtx);
- spin_lock_irq(&ide_lock);
if (hwif->present) {
__ide_port_unregister_devices(hwif);
hwif->present = 0;
}
- spin_unlock_irq(&ide_lock);
ide_proc_unregister_port(hwif);
@@ -340,6 +330,7 @@ static int set_pio_mode_abuse(ide_hwif_t *hwif, u8 req_pio)
static int set_pio_mode(ide_drive_t *drive, int arg)
{
ide_hwif_t *hwif = drive->hwif;
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
const struct ide_port_ops *port_ops = hwif->port_ops;
if (arg < 0 || arg > 255)
@@ -354,9 +345,9 @@ static int set_pio_mode(ide_drive_t *drive, int arg)
unsigned long flags;
/* take lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT */
- spin_lock_irqsave(&ide_lock, flags);
+ spin_lock_irqsave(&hwgroup->lock, flags);
port_ops->set_pio_mode(drive, arg);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
} else
port_ops->set_pio_mode(drive, arg);
} else {
@@ -397,80 +388,6 @@ ide_ext_devset_rw_sync(unmaskirq, unmaskirq);
ide_ext_devset_rw_sync(using_dma, using_dma);
__IDE_DEVSET(pio_mode, DS_SYNC, NULL, set_pio_mode);
-static int generic_ide_suspend(struct device *dev, pm_message_t mesg)
-{
- ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive);
- ide_hwif_t *hwif = HWIF(drive);
- struct request *rq;
- struct request_pm_state rqpm;
- ide_task_t args;
- int ret;
-
- /* call ACPI _GTM only once */
- if ((drive->dn & 1) == 0 || pair == NULL)
- ide_acpi_get_timing(hwif);
-
- memset(&rqpm, 0, sizeof(rqpm));
- memset(&args, 0, sizeof(args));
- rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_PM_SUSPEND;
- rq->special = &args;
- rq->data = &rqpm;
- rqpm.pm_step = IDE_PM_START_SUSPEND;
- if (mesg.event == PM_EVENT_PRETHAW)
- mesg.event = PM_EVENT_FREEZE;
- rqpm.pm_state = mesg.event;
-
- ret = blk_execute_rq(drive->queue, NULL, rq, 0);
- blk_put_request(rq);
-
- /* call ACPI _PS3 only after both devices are suspended */
- if (ret == 0 && ((drive->dn & 1) || pair == NULL))
- ide_acpi_set_state(hwif, 0);
-
- return ret;
-}
-
-static int generic_ide_resume(struct device *dev)
-{
- ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive);
- ide_hwif_t *hwif = HWIF(drive);
- struct request *rq;
- struct request_pm_state rqpm;
- ide_task_t args;
- int err;
-
- /* call ACPI _PS0 / _STM only once */
- if ((drive->dn & 1) == 0 || pair == NULL) {
- ide_acpi_set_state(hwif, 1);
- ide_acpi_push_timing(hwif);
- }
-
- ide_acpi_exec_tfs(drive);
-
- memset(&rqpm, 0, sizeof(rqpm));
- memset(&args, 0, sizeof(args));
- rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_PM_RESUME;
- rq->cmd_flags |= REQ_PREEMPT;
- rq->special = &args;
- rq->data = &rqpm;
- rqpm.pm_step = IDE_PM_START_RESUME;
- rqpm.pm_state = PM_EVENT_ON;
-
- err = blk_execute_rq(drive->queue, NULL, rq, 1);
- blk_put_request(rq);
-
- if (err == 0 && dev->driver) {
- ide_driver_t *drv = to_ide_driver(dev->driver);
-
- if (drv->resume)
- drv->resume(drive);
- }
-
- return err;
-}
-
/**
* ide_device_get - get an additional reference to a ide_drive_t
* @drive: device to get a reference to
diff --git a/drivers/ide/ide_arm.c b/drivers/ide/ide_arm.c
index f728f2927b5a..bdcac94d7c1f 100644
--- a/drivers/ide/ide_arm.c
+++ b/drivers/ide/ide_arm.c
@@ -15,15 +15,8 @@
#define DRV_NAME "ide_arm"
-#ifdef CONFIG_ARCH_CLPS7500
-# include <mach/hardware.h>
-#
-# define IDE_ARM_IO (ISASLOT_IO + 0x1f0)
-# define IDE_ARM_IRQ IRQ_ISA_14
-#else
-# define IDE_ARM_IO 0x1f0
-# define IDE_ARM_IRQ IRQ_HARDDISK
-#endif
+#define IDE_ARM_IO 0x1f0
+#define IDE_ARM_IRQ IRQ_HARDDISK
static int __init ide_arm_init(void)
{
diff --git a/drivers/ide/pdc202xx_old.c b/drivers/ide/pdc202xx_old.c
index 799557c25eef..624e62e5cc9a 100644
--- a/drivers/ide/pdc202xx_old.c
+++ b/drivers/ide/pdc202xx_old.c
@@ -350,16 +350,17 @@ static const struct ide_dma_ops pdc2026x_dma_ops = {
.dma_timeout = pdc202xx_dma_timeout,
};
-#define DECLARE_PDC2026X_DEV(udma, extra_flags) \
+#define DECLARE_PDC2026X_DEV(udma, sectors) \
{ \
.name = DRV_NAME, \
.init_chipset = init_chipset_pdc202xx, \
.port_ops = &pdc2026x_port_ops, \
.dma_ops = &pdc2026x_dma_ops, \
- .host_flags = IDE_HFLAGS_PDC202XX | extra_flags, \
+ .host_flags = IDE_HFLAGS_PDC202XX, \
.pio_mask = ATA_PIO4, \
.mwdma_mask = ATA_MWDMA2, \
.udma_mask = udma, \
+ .max_sectors = sectors, \
}
static const struct ide_port_info pdc202xx_chipsets[] __devinitdata = {
@@ -376,8 +377,8 @@ static const struct ide_port_info pdc202xx_chipsets[] __devinitdata = {
/* 1: PDC2026{2,3} */
DECLARE_PDC2026X_DEV(ATA_UDMA4, 0),
- /* 2: PDC2026{5,7} */
- DECLARE_PDC2026X_DEV(ATA_UDMA5, IDE_HFLAG_RQSIZE_256),
+ /* 2: PDC2026{5,7}: UDMA5, limit LBA48 requests to 256 sectors */
+ DECLARE_PDC2026X_DEV(ATA_UDMA5, 256),
};
/**
diff --git a/drivers/ide/rz1000.c b/drivers/ide/rz1000.c
index 7daf0135cbac..a6414a884eb1 100644
--- a/drivers/ide/rz1000.c
+++ b/drivers/ide/rz1000.c
@@ -22,34 +22,48 @@
#define DRV_NAME "rz1000"
-static void __devinit init_hwif_rz1000 (ide_hwif_t *hwif)
+static int __devinit rz1000_disable_readahead(struct pci_dev *dev)
{
- struct pci_dev *dev = to_pci_dev(hwif->dev);
u16 reg;
if (!pci_read_config_word (dev, 0x40, &reg) &&
!pci_write_config_word(dev, 0x40, reg & 0xdfff)) {
printk(KERN_INFO "%s: disabled chipset read-ahead "
- "(buggy RZ1000/RZ1001)\n", hwif->name);
+ "(buggy RZ1000/RZ1001)\n", pci_name(dev));
+ return 0;
} else {
- if (hwif->mate)
- hwif->mate->serialized = hwif->serialized = 1;
- hwif->host_flags |= IDE_HFLAG_NO_UNMASK_IRQS;
printk(KERN_INFO "%s: serialized, disabled unmasking "
- "(buggy RZ1000/RZ1001)\n", hwif->name);
+ "(buggy RZ1000/RZ1001)\n", pci_name(dev));
+ return 1;
}
}
static const struct ide_port_info rz1000_chipset __devinitdata = {
.name = DRV_NAME,
- .init_hwif = init_hwif_rz1000,
- .chipset = ide_rz1000,
.host_flags = IDE_HFLAG_NO_DMA,
};
static int __devinit rz1000_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- return ide_pci_init_one(dev, &rz1000_chipset, NULL);
+ struct ide_port_info d = rz1000_chipset;
+ int rc;
+
+ rc = pci_enable_device(dev);
+ if (rc)
+ return rc;
+
+ if (rz1000_disable_readahead(dev)) {
+ d.host_flags |= IDE_HFLAG_SERIALIZE;
+ d.host_flags |= IDE_HFLAG_NO_UNMASK_IRQS;
+ }
+
+ return ide_pci_init_one(dev, &d, NULL);
+}
+
+static void rz1000_remove(struct pci_dev *dev)
+{
+ ide_pci_remove(dev);
+ pci_disable_device(dev);
}
static const struct pci_device_id rz1000_pci_tbl[] = {
@@ -63,7 +77,7 @@ static struct pci_driver rz1000_pci_driver = {
.name = "RZ1000_IDE",
.id_table = rz1000_pci_tbl,
.probe = rz1000_init_one,
- .remove = ide_pci_remove,
+ .remove = rz1000_remove,
};
static int __init rz1000_ide_init(void)
diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c
index 75ea61526566..2a5ea90cf8b8 100644
--- a/drivers/ide/trm290.c
+++ b/drivers/ide/trm290.c
@@ -328,10 +328,10 @@ static struct ide_dma_ops trm290_dma_ops = {
static const struct ide_port_info trm290_chipset __devinitdata = {
.name = DRV_NAME,
.init_hwif = init_hwif_trm290,
- .chipset = ide_trm290,
.port_ops = &trm290_port_ops,
.dma_ops = &trm290_dma_ops,
- .host_flags = IDE_HFLAG_NO_ATAPI_DMA |
+ .host_flags = IDE_HFLAG_TRM290 |
+ IDE_HFLAG_NO_ATAPI_DMA |
#if 0 /* play it safe for now */
IDE_HFLAG_TRUST_BIOS_FOR_DMA |
#endif
diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c
index 9120063e8f87..13b63e7fa353 100644
--- a/drivers/ide/tx4938ide.c
+++ b/drivers/ide/tx4938ide.c
@@ -181,7 +181,7 @@ static void tx4938ide_input_data_swap(ide_drive_t *drive, struct request *rq,
while (count--)
*ptr++ = cpu_to_le16(__raw_readw((void __iomem *)port));
- __ide_flush_dcache_range((unsigned long)buf, count * 2);
+ __ide_flush_dcache_range((unsigned long)buf, roundup(len, 2));
}
static void tx4938ide_output_data_swap(ide_drive_t *drive, struct request *rq,
@@ -195,7 +195,7 @@ static void tx4938ide_output_data_swap(ide_drive_t *drive, struct request *rq,
__raw_writew(le16_to_cpu(*ptr), (void __iomem *)port);
ptr++;
}
- __ide_flush_dcache_range((unsigned long)buf, count * 2);
+ __ide_flush_dcache_range((unsigned long)buf, roundup(len, 2));
}
static const struct ide_tp_ops tx4938ide_tp_ops = {
diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c
index bafb7d1a22e2..97cd9e0f66f6 100644
--- a/drivers/ide/tx4939ide.c
+++ b/drivers/ide/tx4939ide.c
@@ -259,6 +259,12 @@ static int tx4939ide_build_dmatable(ide_drive_t *drive, struct request *rq)
bcount = 0x10000 - (cur_addr & 0xffff);
if (bcount > cur_len)
bcount = cur_len;
+ /*
+ * This workaround for zero count seems required.
+ * (standard ide_build_dmatable do it too)
+ */
+ if ((bcount & 0xffff) == 0x0000)
+ bcount = 0x8000;
*table++ = bcount & 0xffff;
*table++ = cur_addr;
cur_addr += bcount;
@@ -558,7 +564,7 @@ static void tx4939ide_input_data_swap(ide_drive_t *drive, struct request *rq,
while (count--)
*ptr++ = cpu_to_le16(__raw_readw((void __iomem *)port));
- __ide_flush_dcache_range((unsigned long)buf, count * 2);
+ __ide_flush_dcache_range((unsigned long)buf, roundup(len, 2));
}
static void tx4939ide_output_data_swap(ide_drive_t *drive, struct request *rq,
@@ -572,7 +578,7 @@ static void tx4939ide_output_data_swap(ide_drive_t *drive, struct request *rq,
__raw_writew(le16_to_cpu(*ptr), (void __iomem *)port);
ptr++;
}
- __ide_flush_dcache_range((unsigned long)buf, count * 2);
+ __ide_flush_dcache_range((unsigned long)buf, roundup(len, 2));
}
static const struct ide_tp_ops tx4939ide_tp_ops = {
diff --git a/drivers/ide/umc8672.c b/drivers/ide/umc8672.c
index 1da076e0c917..e29978cf6197 100644
--- a/drivers/ide/umc8672.c
+++ b/drivers/ide/umc8672.c
@@ -107,18 +107,21 @@ static void umc_set_speeds(u8 speeds[])
static void umc_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
ide_hwif_t *hwif = drive->hwif;
- unsigned long flags;
+ ide_hwgroup_t *mate_hwgroup = hwif->mate ? hwif->mate->hwgroup : NULL;
+ unsigned long uninitialized_var(flags);
printk("%s: setting umc8672 to PIO mode%d (speed %d)\n",
drive->name, pio, pio_to_umc[pio]);
- spin_lock_irqsave(&ide_lock, flags);
- if (hwif->mate && hwif->mate->hwgroup->handler) {
+ if (mate_hwgroup)
+ spin_lock_irqsave(&mate_hwgroup->lock, flags);
+ if (mate_hwgroup && mate_hwgroup->handler) {
printk(KERN_ERR "umc8672: other interface is busy: exiting tune_umc()\n");
} else {
current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
umc_set_speeds(current_speeds);
}
- spin_unlock_irqrestore(&ide_lock, flags);
+ if (mate_hwgroup)
+ spin_unlock_irqrestore(&mate_hwgroup->lock, flags);
}
static const struct ide_port_ops umc8672_port_ops = {
diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
index a5dc78ae62d4..dd0db67bf8d7 100644
--- a/drivers/infiniband/Kconfig
+++ b/drivers/infiniband/Kconfig
@@ -37,6 +37,7 @@ config INFINIBAND_USER_MEM
config INFINIBAND_ADDR_TRANS
bool
depends on INET
+ depends on !(INFINIBAND = y && IPV6 = m)
default y
source "drivers/infiniband/hw/mthca/Kconfig"
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index d98b05b28262..ce511d8748ce 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -128,6 +128,8 @@ int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
ret = rdma_copy_addr(dev_addr, dev, NULL);
dev_put(dev);
break;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
for_each_netdev(&init_net, dev) {
if (ipv6_chk_addr(&init_net,
@@ -138,8 +140,7 @@ int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
}
}
break;
- default:
- break;
+#endif
}
return ret;
}
@@ -179,10 +180,11 @@ static void addr_send_arp(struct sockaddr *dst_in)
{
struct rtable *rt;
struct flowi fl;
- struct dst_entry *dst;
memset(&fl, 0, sizeof fl);
- if (dst_in->sa_family == AF_INET) {
+
+ switch (dst_in->sa_family) {
+ case AF_INET:
fl.nl_u.ip4_u.daddr =
((struct sockaddr_in *) dst_in)->sin_addr.s_addr;
@@ -191,8 +193,13 @@ static void addr_send_arp(struct sockaddr *dst_in)
neigh_event_send(rt->u.dst.neighbour, NULL);
ip_rt_put(rt);
+ break;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ {
+ struct dst_entry *dst;
- } else {
fl.nl_u.ip6_u.daddr =
((struct sockaddr_in6 *) dst_in)->sin6_addr;
@@ -202,6 +209,9 @@ static void addr_send_arp(struct sockaddr *dst_in)
neigh_event_send(dst->neighbour, NULL);
dst_release(dst);
+ break;
+ }
+#endif
}
}
@@ -254,6 +264,7 @@ out:
return ret;
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static int addr6_resolve_remote(struct sockaddr_in6 *src_in,
struct sockaddr_in6 *dst_in,
struct rdma_dev_addr *addr)
@@ -282,6 +293,14 @@ static int addr6_resolve_remote(struct sockaddr_in6 *src_in,
dst_release(dst);
return ret;
}
+#else
+static int addr6_resolve_remote(struct sockaddr_in6 *src_in,
+ struct sockaddr_in6 *dst_in,
+ struct rdma_dev_addr *addr)
+{
+ return -EADDRNOTAVAIL;
+}
+#endif
static int addr_resolve_remote(struct sockaddr *src_in,
struct sockaddr *dst_in,
@@ -340,7 +359,9 @@ static int addr_resolve_local(struct sockaddr *src_in,
struct net_device *dev;
int ret;
- if (dst_in->sa_family == AF_INET) {
+ switch (dst_in->sa_family) {
+ case AF_INET:
+ {
__be32 src_ip = ((struct sockaddr_in *) src_in)->sin_addr.s_addr;
__be32 dst_ip = ((struct sockaddr_in *) dst_in)->sin_addr.s_addr;
@@ -362,7 +383,12 @@ static int addr_resolve_local(struct sockaddr *src_in,
memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
}
dev_put(dev);
- } else {
+ break;
+ }
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ {
struct in6_addr *a;
for_each_netdev(&init_net, dev)
@@ -390,6 +416,13 @@ static int addr_resolve_local(struct sockaddr *src_in,
if (!ret)
memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
}
+ break;
+ }
+#endif
+
+ default:
+ ret = -EADDRNOTAVAIL;
+ break;
}
return ret;
diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c
index 8415ecce5c4c..a3c5af1d7ec0 100644
--- a/drivers/infiniband/hw/mlx4/cq.c
+++ b/drivers/infiniband/hw/mlx4/cq.c
@@ -699,7 +699,7 @@ repoll:
}
wc->slid = be16_to_cpu(cqe->rlid);
- wc->sl = be16_to_cpu(cqe->sl_vid >> 12);
+ wc->sl = be16_to_cpu(cqe->sl_vid) >> 12;
g_mlpath_rqpn = be32_to_cpu(cqe->g_mlpath_rqpn);
wc->src_qp = g_mlpath_rqpn & 0xffffff;
wc->dlid_path_bits = (g_mlpath_rqpn >> 24) & 0x7f;
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 1e5b6446231d..12876392516e 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -119,6 +119,14 @@ error:
iscsi_conn_failure(conn, rc);
}
+static int iscsi_iser_pdu_alloc(struct iscsi_task *task, uint8_t opcode)
+{
+ struct iscsi_iser_task *iser_task = task->dd_data;
+
+ task->hdr = (struct iscsi_hdr *)&iser_task->desc.iscsi_header;
+ task->hdr_max = sizeof(iser_task->desc.iscsi_header);
+ return 0;
+}
/**
* iscsi_iser_task_init - Initialize task
@@ -180,25 +188,26 @@ static int
iscsi_iser_task_xmit_unsol_data(struct iscsi_conn *conn,
struct iscsi_task *task)
{
- struct iscsi_data hdr;
+ struct iscsi_r2t_info *r2t = &task->unsol_r2t;
+ struct iscsi_data hdr;
int error = 0;
/* Send data-out PDUs while there's still unsolicited data to send */
- while (task->unsol_count > 0) {
- iscsi_prep_unsolicit_data_pdu(task, &hdr);
+ while (iscsi_task_has_unsol_data(task)) {
+ iscsi_prep_data_out_pdu(task, r2t, &hdr);
debug_scsi("Sending data-out: itt 0x%x, data count %d\n",
- hdr.itt, task->data_count);
+ hdr.itt, r2t->data_count);
/* the buffer description has been passed with the command */
/* Send the command */
error = iser_send_data_out(conn, task, &hdr);
if (error) {
- task->unsol_datasn--;
+ r2t->datasn--;
goto iscsi_iser_task_xmit_unsol_data_exit;
}
- task->unsol_count -= task->data_count;
+ r2t->sent += r2t->data_count;
debug_scsi("Need to send %d more as data-out PDUs\n",
- task->unsol_count);
+ r2t->data_length - r2t->sent);
}
iscsi_iser_task_xmit_unsol_data_exit:
@@ -220,7 +229,7 @@ iscsi_iser_task_xmit(struct iscsi_task *task)
debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
task->itt, scsi_bufflen(task->sc),
- task->imm_count, task->unsol_count);
+ task->imm_count, task->unsol_r2t.data_length);
}
debug_scsi("task deq [cid %d itt 0x%x]\n",
@@ -235,7 +244,7 @@ iscsi_iser_task_xmit(struct iscsi_task *task)
}
/* Send unsolicited data-out PDU(s) if necessary */
- if (task->unsol_count)
+ if (iscsi_task_has_unsol_data(task))
error = iscsi_iser_task_xmit_unsol_data(conn, task);
iscsi_iser_task_xmit_exit:
@@ -244,13 +253,15 @@ iscsi_iser_task_xmit(struct iscsi_task *task)
return error;
}
-static void
-iscsi_iser_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
+static void iscsi_iser_cleanup_task(struct iscsi_task *task)
{
struct iscsi_iser_task *iser_task = task->dd_data;
- /* mgmt tasks do not need special cleanup */
- if (!task->sc)
+ /*
+ * mgmt tasks do not need special cleanup and we do not
+ * allocate anything in the init task callout
+ */
+ if (!task->sc || task->state == ISCSI_TASK_PENDING)
return;
if (iser_task->status == ISER_TASK_STATUS_STARTED) {
@@ -391,9 +402,6 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep,
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
struct Scsi_Host *shost;
- int i;
- struct iscsi_task *task;
- struct iscsi_iser_task *iser_task;
struct iser_conn *ib_conn;
shost = iscsi_host_alloc(&iscsi_iser_sht, 0, ISCSI_MAX_CMD_PER_LUN);
@@ -430,13 +438,6 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep,
session = cls_session->dd_data;
shost->can_queue = session->scsi_cmds_max;
- /* libiscsi setup itts, data and pool so just set desc fields */
- for (i = 0; i < session->cmds_max; i++) {
- task = session->cmds[i];
- iser_task = task->dd_data;
- task->hdr = (struct iscsi_cmd *)&iser_task->desc.iscsi_header;
- task->hdr_max = sizeof(iser_task->desc.iscsi_header);
- }
return cls_session;
remove_host:
@@ -652,6 +653,7 @@ static struct iscsi_transport iscsi_iser_transport = {
.init_task = iscsi_iser_task_init,
.xmit_task = iscsi_iser_task_xmit,
.cleanup_task = iscsi_iser_cleanup_task,
+ .alloc_pdu = iscsi_iser_pdu_alloc,
/* recovery */
.session_recovery_timedout = iscsi_session_recovery_timedout,
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index ed1aff21b7ea..e209cb8dd948 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -353,8 +353,7 @@ int iser_send_command(struct iscsi_conn *conn,
unsigned long edtl;
int err = 0;
struct iser_data_buf *data_buf;
-
- struct iscsi_cmd *hdr = task->hdr;
+ struct iscsi_cmd *hdr = (struct iscsi_cmd *)task->hdr;
struct scsi_cmnd *sc = task->sc;
if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
@@ -393,7 +392,7 @@ int iser_send_command(struct iscsi_conn *conn,
err = iser_prepare_write_cmd(task,
task->imm_count,
task->imm_count +
- task->unsol_count,
+ task->unsol_r2t.data_length,
edtl);
if (err)
goto send_command_error;
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
index 69e674ecf19a..db22fd9b4cf2 100644
--- a/drivers/input/keyboard/omap-keypad.c
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -101,7 +101,7 @@ static irqreturn_t omap_kp_interrupt(int irq, void *dev_id)
if (cpu_is_omap24xx()) {
int i;
for (i = 0; i < omap_kp->rows; i++)
- disable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+ disable_irq(gpio_to_irq(row_gpios[i]));
} else
/* disable keyboard interrupt and schedule for handling */
omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
@@ -224,7 +224,7 @@ static void omap_kp_tasklet(unsigned long data)
if (cpu_is_omap24xx()) {
int i;
for (i = 0; i < omap_kp_data->rows; i++)
- enable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+ enable_irq(gpio_to_irq(row_gpios[i]));
} else {
omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
kp_cur_group = -1;
@@ -397,7 +397,7 @@ static int __init omap_kp_probe(struct platform_device *pdev)
omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
} else {
for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) {
- if (request_irq(OMAP_GPIO_IRQ(row_gpios[irq_idx]),
+ if (request_irq(gpio_to_irq(row_gpios[irq_idx]),
omap_kp_interrupt,
IRQF_TRIGGER_FALLING,
"omap-keypad", omap_kp) < 0)
@@ -438,7 +438,7 @@ static int omap_kp_remove(struct platform_device *pdev)
gpio_free(col_gpios[i]);
for (i = 0; i < omap_kp->rows; i++) {
gpio_free(row_gpios[i]);
- free_irq(OMAP_GPIO_IRQ(row_gpios[i]), 0);
+ free_irq(gpio_to_irq(row_gpios[i]), 0);
}
} else {
omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c
index 6d30c6d334c3..0d2fc64a5e1c 100644
--- a/drivers/input/keyboard/pxa27x_keypad.c
+++ b/drivers/input/keyboard/pxa27x_keypad.c
@@ -475,7 +475,7 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
goto failed_free_mem;
}
- keypad->clk = clk_get(&pdev->dev, "KBDCLK");
+ keypad->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(keypad->clk)) {
dev_err(&pdev->dev, "failed to get keypad clock\n");
error = PTR_ERR(keypad->clk);
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 27d70d326ff3..da3c3a5d2689 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -79,7 +79,7 @@ config SERIO_PARKBD
config SERIO_RPCKBD
tristate "Acorn RiscPC keyboard controller"
- depends on ARCH_ACORN || ARCH_CLPS7500
+ depends on ARCH_ACORN
default y
help
Say Y here if you have the Acorn RiscPC and want to use an AT
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index b9b7fc6ff1eb..e1ece89fe922 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -697,7 +697,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
struct ads7846 *ts = container_of(handle, struct ads7846, timer);
int status = 0;
- spin_lock_irq(&ts->lock);
+ spin_lock(&ts->lock);
if (unlikely(!get_pendown_state(ts) ||
device_suspended(&ts->spi->dev))) {
@@ -728,7 +728,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
dev_err(&ts->spi->dev, "spi_async --> %d\n", status);
}
- spin_unlock_irq(&ts->lock);
+ spin_unlock(&ts->lock);
return HRTIMER_NORESTART;
}
diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c
index ba648750a8d9..1d11e2be9ef8 100644
--- a/drivers/input/touchscreen/mainstone-wm97xx.c
+++ b/drivers/input/touchscreen/mainstone-wm97xx.c
@@ -31,7 +31,7 @@
#include <linux/interrupt.h>
#include <linux/wm97xx.h>
#include <linux/io.h>
-#include <mach/pxa-regs.h>
+#include <mach/regs-ac97.h>
#define VERSION "0.13"
diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h
index 5faefeaf6790..f2c641e0bdde 100644
--- a/drivers/lguest/lg.h
+++ b/drivers/lguest/lg.h
@@ -164,7 +164,7 @@ void copy_gdt(const struct lg_cpu *cpu, struct desc_struct *gdt);
void copy_gdt_tls(const struct lg_cpu *cpu, struct desc_struct *gdt);
/* page_tables.c: */
-int init_guest_pagetable(struct lguest *lg, unsigned long pgtable);
+int init_guest_pagetable(struct lguest *lg);
void free_guest_pagetable(struct lguest *lg);
void guest_new_pagetable(struct lg_cpu *cpu, unsigned long pgtable);
void guest_set_pmd(struct lguest *lg, unsigned long gpgdir, u32 i);
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c
index a661bbdae3d6..915da6b8c924 100644
--- a/drivers/lguest/lguest_device.c
+++ b/drivers/lguest/lguest_device.c
@@ -250,7 +250,7 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
/* Figure out how many pages the ring will take, and map that memory */
lvq->pages = lguest_map((unsigned long)lvq->config.pfn << PAGE_SHIFT,
DIV_ROUND_UP(vring_size(lvq->config.num,
- PAGE_SIZE),
+ LGUEST_VRING_ALIGN),
PAGE_SIZE));
if (!lvq->pages) {
err = -ENOMEM;
@@ -259,8 +259,8 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
/* OK, tell virtio_ring.c to set up a virtqueue now we know its size
* and we've got a pointer to its pages. */
- vq = vring_new_virtqueue(lvq->config.num, vdev, lvq->pages,
- lg_notify, callback);
+ vq = vring_new_virtqueue(lvq->config.num, LGUEST_VRING_ALIGN,
+ vdev, lvq->pages, lg_notify, callback);
if (!vq) {
err = -ENOMEM;
goto unmap;
@@ -272,7 +272,7 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
* the interrupt as a source of randomness: it'd be nice to have that
* back.. */
err = request_irq(lvq->config.irq, vring_interrupt, IRQF_SHARED,
- vdev->dev.bus_id, vq);
+ dev_name(&vdev->dev), vq);
if (err)
goto destroy_vring;
diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c
index e73a000473cc..34bc017b8b3c 100644
--- a/drivers/lguest/lguest_user.c
+++ b/drivers/lguest/lguest_user.c
@@ -146,7 +146,7 @@ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip)
return 0;
}
-/*L:020 The initialization write supplies 4 pointer sized (32 or 64 bit)
+/*L:020 The initialization write supplies 3 pointer sized (32 or 64 bit)
* values (in addition to the LHREQ_INITIALIZE value). These are:
*
* base: The start of the Guest-physical memory inside the Launcher memory.
@@ -155,9 +155,6 @@ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip)
* allowed to access. The Guest memory lives inside the Launcher, so it sets
* this to ensure the Guest can only reach its own memory.
*
- * pgdir: The (Guest-physical) address of the top of the initial Guest
- * pagetables (which are set up by the Launcher).
- *
* start: The first instruction to execute ("eip" in x86-speak).
*/
static int initialize(struct file *file, const unsigned long __user *input)
@@ -166,7 +163,7 @@ static int initialize(struct file *file, const unsigned long __user *input)
* Guest. */
struct lguest *lg;
int err;
- unsigned long args[4];
+ unsigned long args[3];
/* We grab the Big Lguest lock, which protects against multiple
* simultaneous initializations. */
@@ -192,14 +189,14 @@ static int initialize(struct file *file, const unsigned long __user *input)
lg->mem_base = (void __user *)args[0];
lg->pfn_limit = args[1];
- /* This is the first cpu (cpu 0) and it will start booting at args[3] */
- err = lg_cpu_start(&lg->cpus[0], 0, args[3]);
+ /* This is the first cpu (cpu 0) and it will start booting at args[2] */
+ err = lg_cpu_start(&lg->cpus[0], 0, args[2]);
if (err)
goto release_guest;
/* Initialize the Guest's shadow page tables, using the toplevel
* address the Launcher gave us. This allocates memory, so can fail. */
- err = init_guest_pagetable(lg, args[2]);
+ err = init_guest_pagetable(lg);
if (err)
goto free_regs;
diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c
index 81d0c6053447..576a8318221c 100644
--- a/drivers/lguest/page_tables.c
+++ b/drivers/lguest/page_tables.c
@@ -14,6 +14,7 @@
#include <linux/percpu.h>
#include <asm/tlbflush.h>
#include <asm/uaccess.h>
+#include <asm/bootparam.h>
#include "lg.h"
/*M:008 We hold reference to pages, which prevents them from being swapped.
@@ -581,15 +582,82 @@ void guest_set_pmd(struct lguest *lg, unsigned long gpgdir, u32 idx)
release_pgd(lg, lg->pgdirs[pgdir].pgdir + idx);
}
+/* Once we know how much memory we have we can construct simple identity
+ * (which set virtual == physical) and linear mappings
+ * which will get the Guest far enough into the boot to create its own.
+ *
+ * We lay them out of the way, just below the initrd (which is why we need to
+ * know its size here). */
+static unsigned long setup_pagetables(struct lguest *lg,
+ unsigned long mem,
+ unsigned long initrd_size)
+{
+ pgd_t __user *pgdir;
+ pte_t __user *linear;
+ unsigned int mapped_pages, i, linear_pages, phys_linear;
+ unsigned long mem_base = (unsigned long)lg->mem_base;
+
+ /* We have mapped_pages frames to map, so we need
+ * linear_pages page tables to map them. */
+ mapped_pages = mem / PAGE_SIZE;
+ linear_pages = (mapped_pages + PTRS_PER_PTE - 1) / PTRS_PER_PTE;
+
+ /* We put the toplevel page directory page at the top of memory. */
+ pgdir = (pgd_t *)(mem + mem_base - initrd_size - PAGE_SIZE);
+
+ /* Now we use the next linear_pages pages as pte pages */
+ linear = (void *)pgdir - linear_pages * PAGE_SIZE;
+
+ /* Linear mapping is easy: put every page's address into the
+ * mapping in order. */
+ for (i = 0; i < mapped_pages; i++) {
+ pte_t pte;
+ pte = pfn_pte(i, __pgprot(_PAGE_PRESENT|_PAGE_RW|_PAGE_USER));
+ if (copy_to_user(&linear[i], &pte, sizeof(pte)) != 0)
+ return -EFAULT;
+ }
+
+ /* The top level points to the linear page table pages above.
+ * We setup the identity and linear mappings here. */
+ phys_linear = (unsigned long)linear - mem_base;
+ for (i = 0; i < mapped_pages; i += PTRS_PER_PTE) {
+ pgd_t pgd;
+ pgd = __pgd((phys_linear + i * sizeof(pte_t)) |
+ (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER));
+
+ if (copy_to_user(&pgdir[i / PTRS_PER_PTE], &pgd, sizeof(pgd))
+ || copy_to_user(&pgdir[pgd_index(PAGE_OFFSET)
+ + i / PTRS_PER_PTE],
+ &pgd, sizeof(pgd)))
+ return -EFAULT;
+ }
+
+ /* We return the top level (guest-physical) address: remember where
+ * this is. */
+ return (unsigned long)pgdir - mem_base;
+}
+
/*H:500 (vii) Setting up the page tables initially.
*
* When a Guest is first created, the Launcher tells us where the toplevel of
* its first page table is. We set some things up here: */
-int init_guest_pagetable(struct lguest *lg, unsigned long pgtable)
+int init_guest_pagetable(struct lguest *lg)
{
+ u64 mem;
+ u32 initrd_size;
+ struct boot_params __user *boot = (struct boot_params *)lg->mem_base;
+
+ /* Get the Guest memory size and the ramdisk size from the boot header
+ * located at lg->mem_base (Guest address 0). */
+ if (copy_from_user(&mem, &boot->e820_map[0].size, sizeof(mem))
+ || get_user(initrd_size, &boot->hdr.ramdisk_size))
+ return -EFAULT;
+
/* We start on the first shadow page table, and give it a blank PGD
* page. */
- lg->pgdirs[0].gpgdir = pgtable;
+ lg->pgdirs[0].gpgdir = setup_pagetables(lg, mem, initrd_size);
+ if (IS_ERR_VALUE(lg->pgdirs[0].gpgdir))
+ return lg->pgdirs[0].gpgdir;
lg->pgdirs[0].pgdir = (pgd_t *)get_zeroed_page(GFP_KERNEL);
if (!lg->pgdirs[0].pgdir)
return -ENOMEM;
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index ce26c84af064..3326750ec02c 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1060,7 +1060,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad_page_pool;
}
- cc->bs = bioset_create(MIN_IOS, MIN_IOS);
+ cc->bs = bioset_create(MIN_IOS, 0);
if (!cc->bs) {
ti->error = "Cannot allocate crypt bioset";
goto bad_bs;
diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c
index 2fd6d4450637..a34338567a2a 100644
--- a/drivers/md/dm-io.c
+++ b/drivers/md/dm-io.c
@@ -56,7 +56,7 @@ struct dm_io_client *dm_io_client_create(unsigned num_pages)
if (!client->pool)
goto bad;
- client->bios = bioset_create(16, 16);
+ client->bios = bioset_create(16, 0);
if (!client->bios)
goto bad;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 343094c3feeb..421c9f02d8ca 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1093,7 +1093,7 @@ static struct mapped_device *alloc_dev(int minor)
if (!md->tio_pool)
goto bad_tio_pool;
- md->bs = bioset_create(16, 16);
+ md->bs = bioset_create(16, 0);
if (!md->bs)
goto bad_no_bioset;
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c
index 4952aeb5dd80..d8229a0e9a9c 100644
--- a/drivers/media/common/ir-keymaps.c
+++ b/drivers/media/common/ir-keymaps.c
@@ -2391,6 +2391,67 @@ IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE] = {
};
EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel);
+/* Kworld Plus TV Analog Lite PCI IR
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+ */
+IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE] = {
+ [0x0c] = KEY_PROG1, /* Kworld key */
+ [0x16] = KEY_CLOSECD, /* -> ) */
+ [0x1d] = KEY_POWER2,
+
+ [0x00] = KEY_1,
+ [0x01] = KEY_2,
+ [0x02] = KEY_3, /* Two keys have the same code: 3 and left */
+ [0x03] = KEY_4, /* Two keys have the same code: 3 and right */
+ [0x04] = KEY_5,
+ [0x05] = KEY_6,
+ [0x06] = KEY_7,
+ [0x07] = KEY_8,
+ [0x08] = KEY_9,
+ [0x0a] = KEY_0,
+
+ [0x09] = KEY_AGAIN,
+ [0x14] = KEY_MUTE,
+
+ [0x20] = KEY_UP,
+ [0x21] = KEY_DOWN,
+ [0x0b] = KEY_ENTER,
+
+ [0x10] = KEY_CHANNELUP,
+ [0x11] = KEY_CHANNELDOWN,
+
+ /* Couldn't map key left/key right since those
+ conflict with '3' and '4' scancodes
+ I dunno what the original driver does
+ */
+
+ [0x13] = KEY_VOLUMEUP,
+ [0x12] = KEY_VOLUMEDOWN,
+
+ /* The lower part of the IR
+ There are several duplicated keycodes there.
+ Most of them conflict with digits.
+ Add mappings just to the unused scancodes.
+ Somehow, the original driver has a way to know,
+ but this doesn't seem to be on some GPIO.
+ Also, it is not related to the time between keyup
+ and keydown.
+ */
+ [0x19] = KEY_PAUSE, /* Timeshift */
+ [0x1a] = KEY_STOP,
+ [0x1b] = KEY_RECORD,
+
+ [0x22] = KEY_TEXT,
+
+ [0x15] = KEY_AUDIO, /* ((*)) */
+ [0x0f] = KEY_ZOOM,
+ [0x1c] = KEY_SHUFFLE, /* snapshot */
+
+ [0x18] = KEY_RED, /* B */
+ [0x23] = KEY_GREEN, /* C */
+};
+EXPORT_SYMBOL_GPL(ir_codes_kworld_plus_tv_analog);
+
IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = {
[0x20] = KEY_LIST,
[0x00] = KEY_POWER,
@@ -2511,3 +2572,35 @@ IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE] = {
};
EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys);
+
+/* ATI TV Wonder HD 600 USB
+ Devin Heitmueller <devin.heitmueller@gmail.com>
+ */
+IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE] = {
+ [0x00] = KEY_RECORD, /* Row 1 */
+ [0x01] = KEY_PLAYPAUSE,
+ [0x02] = KEY_STOP,
+ [0x03] = KEY_POWER,
+ [0x04] = KEY_PREVIOUS, /* Row 2 */
+ [0x05] = KEY_REWIND,
+ [0x06] = KEY_FORWARD,
+ [0x07] = KEY_NEXT,
+ [0x08] = KEY_EPG, /* Row 3 */
+ [0x09] = KEY_HOME,
+ [0x0a] = KEY_MENU,
+ [0x0b] = KEY_CHANNELUP,
+ [0x0c] = KEY_BACK, /* Row 4 */
+ [0x0d] = KEY_UP,
+ [0x0e] = KEY_INFO,
+ [0x0f] = KEY_CHANNELDOWN,
+ [0x10] = KEY_LEFT, /* Row 5 */
+ [0x11] = KEY_SELECT,
+ [0x12] = KEY_RIGHT,
+ [0x13] = KEY_VOLUMEUP,
+ [0x14] = KEY_LAST, /* Row 6 */
+ [0x15] = KEY_DOWN,
+ [0x16] = KEY_MUTE,
+ [0x17] = KEY_VOLUMEDOWN,
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_ati_tv_wonder_hd_600);
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index 127b0526a727..7d844af88384 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -313,7 +313,7 @@ static int fops_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
/*
DEB_EE(("inode:%p, file:%p, cmd:%d, arg:%li\n",inode, file, cmd, arg));
*/
- return video_usercopy(inode, file, cmd, arg, saa7146_video_do_ioctl);
+ return video_usercopy(file, cmd, arg, saa7146_video_do_ioctl);
}
static int fops_mmap(struct file *file, struct vm_area_struct * vma)
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
index fe0bd55977e3..101b01dbb8ea 100644
--- a/drivers/media/common/saa7146_video.c
+++ b/drivers/media/common/saa7146_video.c
@@ -834,7 +834,7 @@ static int video_end(struct saa7146_fh *fh, struct file *file)
* copying is done already, arg is a kernel pointer.
*/
-static int __saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+int saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
@@ -1216,17 +1216,11 @@ static int __saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *a
#endif
default:
return v4l_compat_translate_ioctl(file, cmd, arg,
- __saa7146_video_do_ioctl);
+ saa7146_video_do_ioctl);
}
return 0;
}
-int saa7146_video_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
-{
- return __saa7146_video_do_ioctl(file, cmd, arg);
-}
-
/*********************************************************************************/
/* buffer handling functions */
diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c
index a8878244bb3c..31522d2e318e 100644
--- a/drivers/media/common/tuners/mxl5005s.c
+++ b/drivers/media/common/tuners/mxl5005s.c
@@ -3598,7 +3598,7 @@ static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum,
76, 77, 91, 134, 135, 137, 147,
156, 166, 167, 168, 25 };
- *count = sizeof(RegAddr) / sizeof(u8);
+ *count = ARRAY_SIZE(RegAddr);
status += MXL_BlockInit(fe);
@@ -3630,7 +3630,7 @@ static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, u8 *RegVal,
*/
#endif
- *count = sizeof(RegAddr) / sizeof(u8);
+ *count = ARRAY_SIZE(RegAddr);
for (i = 0 ; i < *count; i++) {
RegNum[i] = RegAddr[i];
@@ -3648,7 +3648,7 @@ static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum,
u8 RegAddr[] = {43, 136};
- *count = sizeof(RegAddr) / sizeof(u8);
+ *count = ARRAY_SIZE(RegAddr);
for (i = 0; i < *count; i++) {
RegNum[i] = RegAddr[i];
diff --git a/drivers/media/common/tuners/tda827x.c b/drivers/media/common/tuners/tda827x.c
index 4a74f65e759a..f4d931f14fad 100644
--- a/drivers/media/common/tuners/tda827x.c
+++ b/drivers/media/common/tuners/tda827x.c
@@ -80,10 +80,11 @@ static void tda827x_set_std(struct dvb_frontend *fe,
mode = "xx";
}
- if (params->mode == V4L2_TUNER_RADIO)
+ if (params->mode == V4L2_TUNER_RADIO) {
priv->sgIF = 88; /* if frequency is 5.5 MHz */
-
- dprintk("setting tda827x to system %s\n", mode);
+ dprintk("setting tda827x to radio FM\n");
+ } else
+ dprintk("setting tda827x to system %s\n", mode);
}
@@ -199,7 +200,7 @@ static int tda827xo_set_params(struct dvb_frontend *fe,
fe->ops.i2c_gate_ctrl(fe, 1);
i2c_transfer(priv->i2c_adap, &msg, 1);
- priv->frequency = tuner_freq - if_freq; // FIXME
+ priv->frequency = params->frequency;
priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
return 0;
@@ -304,7 +305,7 @@ static int tda827xo_set_analog_params(struct dvb_frontend *fe,
reg2[1] = 0x08; /* Vsync en */
i2c_transfer(priv->i2c_adap, &msg, 1);
- priv->frequency = freq * 62500;
+ priv->frequency = params->frequency;
return 0;
}
@@ -591,7 +592,7 @@ static int tda827xa_set_params(struct dvb_frontend *fe,
fe->ops.i2c_gate_ctrl(fe, 1);
i2c_transfer(priv->i2c_adap, &msg, 1);
- priv->frequency = tuner_freq - if_freq; // FIXME
+ priv->frequency = params->frequency;
priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
return 0;
@@ -691,7 +692,7 @@ static int tda827xa_set_analog_params(struct dvb_frontend *fe,
tuner_reg[1] = 0x19 + (priv->lpsel << 1);
i2c_transfer(priv->i2c_adap, &msg, 1);
- priv->frequency = freq * 62500;
+ priv->frequency = params->frequency;
return 0;
}
diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c
index c112bdd4e0f0..0ee79fd7c7a9 100644
--- a/drivers/media/common/tuners/tda8290.c
+++ b/drivers/media/common/tuners/tda8290.c
@@ -32,6 +32,9 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");
+static int deemphasis_50;
+MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis");
+
/* ---------------------------------------------------------------------- */
struct tda8290_priv {
@@ -139,9 +142,34 @@ static void set_audio(struct dvb_frontend *fe,
mode = "xx";
}
- tuner_dbg("setting tda829x to system %s\n", mode);
+ if (params->mode == V4L2_TUNER_RADIO) {
+ priv->tda8290_easy_mode = 0x01; /* Start with MN values */
+ tuner_dbg("setting to radio FM\n");
+ } else {
+ tuner_dbg("setting tda829x to system %s\n", mode);
+ }
}
+struct {
+ unsigned char seq[2];
+} fm_mode[] = {
+ { { 0x01, 0x81} }, /* Put device into expert mode */
+ { { 0x03, 0x48} }, /* Disable NOTCH and VIDEO filters */
+ { { 0x04, 0x04} }, /* Disable color carrier filter (SSIF) */
+ { { 0x05, 0x04} }, /* ADC headroom */
+ { { 0x06, 0x10} }, /* group delay flat */
+
+ { { 0x07, 0x00} }, /* use the same radio DTO values as a tda8295 */
+ { { 0x08, 0x00} },
+ { { 0x09, 0x80} },
+ { { 0x0a, 0xda} },
+ { { 0x0b, 0x4b} },
+ { { 0x0c, 0x68} },
+
+ { { 0x0d, 0x00} }, /* PLL off, no video carrier detect */
+ { { 0x14, 0x00} }, /* disable auto mute if no video */
+};
+
static void tda8290_set_params(struct dvb_frontend *fe,
struct analog_parameters *params)
{
@@ -178,15 +206,30 @@ static void tda8290_set_params(struct dvb_frontend *fe,
tuner_i2c_xfer_send(&priv->i2c_props, soft_reset, 2);
msleep(1);
- expert_mode[1] = priv->tda8290_easy_mode + 0x80;
- tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2);
- if (priv->tda8290_easy_mode & 0x60)
- tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2);
- else
- tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2);
+ if (params->mode == V4L2_TUNER_RADIO) {
+ int i;
+ unsigned char deemphasis[] = { 0x13, 1 };
+
+ /* FIXME: allow using a different deemphasis */
+
+ if (deemphasis_50)
+ deemphasis[1] = 2;
+
+ for (i = 0; i < ARRAY_SIZE(fm_mode); i++)
+ tuner_i2c_xfer_send(&priv->i2c_props, fm_mode[i].seq, 2);
+
+ tuner_i2c_xfer_send(&priv->i2c_props, deemphasis, 2);
+ } else {
+ expert_mode[1] = priv->tda8290_easy_mode + 0x80;
+ tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2);
+ if (priv->tda8290_easy_mode & 0x60)
+ tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2);
+ else
+ tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2);
+ }
tda8290_i2c_bridge(fe, 1);
diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c
index ff1788cc5d48..544cdbe88a6c 100644
--- a/drivers/media/common/tuners/tda9887.c
+++ b/drivers/media/common/tuners/tda9887.c
@@ -180,11 +180,10 @@ static struct tvnorm tvnorms[] = {
},{
.std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
.name = "SECAM-BGH",
- .b = ( cPositiveAmTV |
+ .b = ( cNegativeFmTV |
cQSS ),
.c = ( cTopDefault),
- .e = ( cGating_36 |
- cAudioIF_5_5 |
+ .e = ( cAudioIF_5_5 |
cVideoIF_38_90 ),
},{
.std = V4L2_STD_SECAM_L,
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
index b65e6803e6c6..1adce9ff52ce 100644
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ b/drivers/media/common/tuners/tuner-xc2028.c
@@ -28,6 +28,12 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");
+static int no_poweroff;
+module_param(no_poweroff, int, 0644);
+MODULE_PARM_DESC(debug, "0 (default) powers device off when not used.\n"
+ "1 keep device energized and with tuner ready all the times.\n"
+ " Faster, but consumes more power and keeps the device hotter\n");
+
static char audio_std[8];
module_param_string(audio_std, audio_std, sizeof(audio_std), 0);
MODULE_PARM_DESC(audio_std,
@@ -1091,6 +1097,34 @@ static int xc2028_set_params(struct dvb_frontend *fe,
T_DIGITAL_TV, type, 0, demod);
}
+static int xc2028_sleep(struct dvb_frontend *fe)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int rc = 0;
+
+ /* Avoid firmware reload on slow devices */
+ if (no_poweroff)
+ return 0;
+
+ tuner_dbg("Putting xc2028/3028 into poweroff mode.\n");
+ if (debug > 1) {
+ tuner_dbg("Printing sleep stack trace:\n");
+ dump_stack();
+ }
+
+ mutex_lock(&priv->lock);
+
+ if (priv->firm_version < 0x0202)
+ rc = send_seq(priv, {0x00, 0x08, 0x00, 0x00});
+ else
+ rc = send_seq(priv, {0x80, 0x08, 0x00, 0x00});
+
+ priv->cur_fw.type = 0; /* need firmware reload */
+
+ mutex_unlock(&priv->lock);
+
+ return rc;
+}
static int xc2028_dvb_release(struct dvb_frontend *fe)
{
@@ -1171,6 +1205,7 @@ static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
.get_frequency = xc2028_get_frequency,
.get_rf_strength = xc2028_signal,
.set_params = xc2028_set_params,
+ .sleep = xc2028_sleep,
};
struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index e12d13e0cbe9..493ce93caf43 100644
--- a/drivers/media/common/tuners/xc5000.c
+++ b/drivers/media/common/tuners/xc5000.c
@@ -36,10 +36,6 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-static int xc5000_load_fw_on_attach;
-module_param_named(init_fw, xc5000_load_fw_on_attach, int, 0644);
-MODULE_PARM_DESC(init_fw, "Load firmware during driver initialization.");
-
static DEFINE_MUTEX(xc5000_list_mutex);
static LIST_HEAD(hybrid_tuner_instance_list);
@@ -1017,9 +1013,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops,
sizeof(struct dvb_tuner_ops));
- if (xc5000_load_fw_on_attach)
- xc5000_init(fe);
-
return fe;
fail:
mutex_unlock(&xc5000_list_mutex);
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index 0bcd852576d6..40ebde53b3ce 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -2,6 +2,19 @@
# DVB device configuration
#
+config DVB_DYNAMIC_MINORS
+ bool "Dynamic DVB minor allocation"
+ depends on DVB_CORE
+ default n
+ help
+ If you say Y here, the DVB subsystem will use dynamic minor
+ allocation for any device that uses the DVB major number.
+ This means that you can have more than 4 of a single type
+ of device (like demuxes and frontends) per adapter, but udev
+ will be required to manage the device nodes.
+
+ If you are unsure about this, say N here.
+
menuconfig DVB_CAPTURE_DRIVERS
bool "DVB/ATSC adapters"
depends on DVB_CORE
diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig
index b34301d56cd2..a8c6249c4099 100644
--- a/drivers/media/dvb/b2c2/Kconfig
+++ b/drivers/media/dvb/b2c2/Kconfig
@@ -14,6 +14,7 @@ config DVB_B2C2_FLEXCOP
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
select DVB_CX24123 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
+ select DVB_TUNER_CX24113 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/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c
index a21ce9edcc7e..f48f73aff195 100644
--- a/drivers/media/dvb/dm1105/dm1105.c
+++ b/drivers/media/dvb/dm1105/dm1105.c
@@ -19,7 +19,6 @@
*
*/
-#include <linux/version.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -368,7 +367,7 @@ static int __devinit dm1105dvb_dma_map(struct dm1105dvb *dm1105dvb)
{
dm1105dvb->ts_buf = pci_alloc_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, &dm1105dvb->dma_addr);
- return pci_dma_mapping_error(dm1105dvb->pdev, dm1105dvb->dma_addr);
+ return !dm1105dvb->ts_buf;
}
static void dm1105dvb_dma_unmap(struct dm1105dvb *dm1105dvb)
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 7a421e9dba5a..171f9ca124f7 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -128,6 +128,7 @@ struct dvb_frontend_private {
unsigned int step_size;
int quality;
unsigned int check_wrapped;
+ enum dvbfe_search algo_status;
};
static void dvb_frontend_wakeup(struct dvb_frontend *fe);
@@ -516,6 +517,8 @@ static int dvb_frontend_thread(void *data)
struct dvb_frontend_private *fepriv = fe->frontend_priv;
unsigned long timeout;
fe_status_t s;
+ enum dvbfe_algo algo;
+
struct dvb_frontend_parameters *params;
dprintk("%s\n", __func__);
@@ -562,23 +565,80 @@ restart:
/* do an iteration of the tuning loop */
if (fe->ops.get_frontend_algo) {
- if (fe->ops.get_frontend_algo(fe) == FE_ALGO_HW) {
- /* have we been asked to retune? */
- params = NULL;
+ algo = fe->ops.get_frontend_algo(fe);
+ switch (algo) {
+ case DVBFE_ALGO_HW:
+ dprintk("%s: Frontend ALGO = DVBFE_ALGO_HW\n", __func__);
+ params = NULL; /* have we been asked to RETUNE ? */
+
if (fepriv->state & FESTATE_RETUNE) {
+ dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__);
params = &fepriv->parameters;
fepriv->state = FESTATE_TUNED;
}
- fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s);
- if (s != fepriv->status) {
+ if (fe->ops.tune)
+ fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s);
+
+ if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) {
+ dprintk("%s: state changed, adding current state\n", __func__);
dvb_frontend_add_event(fe, s);
fepriv->status = s;
}
- } else
+ break;
+ case DVBFE_ALGO_SW:
+ dprintk("%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__);
dvb_frontend_swzigzag(fe);
- } else
+ break;
+ case DVBFE_ALGO_CUSTOM:
+ params = NULL; /* have we been asked to RETUNE ? */
+ dprintk("%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state);
+ if (fepriv->state & FESTATE_RETUNE) {
+ dprintk("%s: Retune requested, FESTAT_RETUNE\n", __func__);
+ params = &fepriv->parameters;
+ fepriv->state = FESTATE_TUNED;
+ }
+ /* Case where we are going to search for a carrier
+ * User asked us to retune again for some reason, possibly
+ * requesting a search with a new set of parameters
+ */
+ if (fepriv->algo_status & DVBFE_ALGO_SEARCH_AGAIN) {
+ if (fe->ops.search) {
+ fepriv->algo_status = fe->ops.search(fe, &fepriv->parameters);
+ /* We did do a search as was requested, the flags are
+ * now unset as well and has the flags wrt to search.
+ */
+ } else {
+ fepriv->algo_status &= ~DVBFE_ALGO_SEARCH_AGAIN;
+ }
+ }
+ /* Track the carrier if the search was successful */
+ if (fepriv->algo_status == DVBFE_ALGO_SEARCH_SUCCESS) {
+ if (fe->ops.track)
+ fe->ops.track(fe, &fepriv->parameters);
+ } else {
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+ fepriv->delay = HZ / 2;
+ }
+ fe->ops.read_status(fe, &s);
+ if (s != fepriv->status) {
+ dvb_frontend_add_event(fe, s); /* update event list */
+ fepriv->status = s;
+ if (!(s & FE_HAS_LOCK)) {
+ fepriv->delay = HZ / 10;
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+ } else {
+ fepriv->delay = 60 * HZ;
+ }
+ }
+ break;
+ default:
+ dprintk("%s: UNDEFINED ALGO !\n", __func__);
+ break;
+ }
+ } else {
dvb_frontend_swzigzag(fe);
+ }
}
if (dvb_powerdown_on_sleep) {
@@ -1226,6 +1286,9 @@ int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp,
dprintk("%s() Finalised property cache\n", __func__);
dtv_property_cache_submit(fe);
+ /* Request the search algorithm to search */
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+
r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND,
&fepriv->parameters);
break;
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index db4a63b0a32e..e176da472d7a 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -69,6 +69,125 @@ struct analog_parameters {
u64 std;
};
+enum dvbfe_modcod {
+ DVBFE_MODCOD_DUMMY_PLFRAME = 0,
+ DVBFE_MODCOD_QPSK_1_4,
+ DVBFE_MODCOD_QPSK_1_3,
+ DVBFE_MODCOD_QPSK_2_5,
+ DVBFE_MODCOD_QPSK_1_2,
+ DVBFE_MODCOD_QPSK_3_5,
+ DVBFE_MODCOD_QPSK_2_3,
+ DVBFE_MODCOD_QPSK_3_4,
+ DVBFE_MODCOD_QPSK_4_5,
+ DVBFE_MODCOD_QPSK_5_6,
+ DVBFE_MODCOD_QPSK_8_9,
+ DVBFE_MODCOD_QPSK_9_10,
+ DVBFE_MODCOD_8PSK_3_5,
+ DVBFE_MODCOD_8PSK_2_3,
+ DVBFE_MODCOD_8PSK_3_4,
+ DVBFE_MODCOD_8PSK_5_6,
+ DVBFE_MODCOD_8PSK_8_9,
+ DVBFE_MODCOD_8PSK_9_10,
+ DVBFE_MODCOD_16APSK_2_3,
+ DVBFE_MODCOD_16APSK_3_4,
+ DVBFE_MODCOD_16APSK_4_5,
+ DVBFE_MODCOD_16APSK_5_6,
+ DVBFE_MODCOD_16APSK_8_9,
+ DVBFE_MODCOD_16APSK_9_10,
+ DVBFE_MODCOD_32APSK_3_4,
+ DVBFE_MODCOD_32APSK_4_5,
+ DVBFE_MODCOD_32APSK_5_6,
+ DVBFE_MODCOD_32APSK_8_9,
+ DVBFE_MODCOD_32APSK_9_10,
+ DVBFE_MODCOD_RESERVED_1,
+ DVBFE_MODCOD_BPSK_1_3,
+ DVBFE_MODCOD_BPSK_1_4,
+ DVBFE_MODCOD_RESERVED_2
+};
+
+enum tuner_param {
+ DVBFE_TUNER_FREQUENCY = (1 << 0),
+ DVBFE_TUNER_TUNERSTEP = (1 << 1),
+ DVBFE_TUNER_IFFREQ = (1 << 2),
+ DVBFE_TUNER_BANDWIDTH = (1 << 3),
+ DVBFE_TUNER_REFCLOCK = (1 << 4),
+ DVBFE_TUNER_IQSENSE = (1 << 5),
+ DVBFE_TUNER_DUMMY = (1 << 31)
+};
+
+/*
+ * ALGO_HW: (Hardware Algorithm)
+ * ----------------------------------------------------------------
+ * Devices that support this algorithm do everything in hardware
+ * and no software support is needed to handle them.
+ * Requesting these devices to LOCK is the only thing required,
+ * device is supposed to do everything in the hardware.
+ *
+ * ALGO_SW: (Software Algorithm)
+ * ----------------------------------------------------------------
+ * These are dumb devices, that require software to do everything
+ *
+ * ALGO_CUSTOM: (Customizable Agorithm)
+ * ----------------------------------------------------------------
+ * Devices having this algorithm can be customized to have specific
+ * algorithms in the frontend driver, rather than simply doing a
+ * software zig-zag. In this case the zigzag maybe hardware assisted
+ * or it maybe completely done in hardware. In all cases, usage of
+ * this algorithm, in conjunction with the search and track
+ * callbacks, utilizes the driver specific algorithm.
+ *
+ * ALGO_RECOVERY: (Recovery Algorithm)
+ * ----------------------------------------------------------------
+ * These devices have AUTO recovery capabilities from LOCK failure
+ */
+enum dvbfe_algo {
+ DVBFE_ALGO_HW = (1 << 0),
+ DVBFE_ALGO_SW = (1 << 1),
+ DVBFE_ALGO_CUSTOM = (1 << 2),
+ DVBFE_ALGO_RECOVERY = (1 << 31)
+};
+
+struct tuner_state {
+ u32 frequency;
+ u32 tunerstep;
+ u32 ifreq;
+ u32 bandwidth;
+ u32 iqsense;
+ u32 refclock;
+};
+
+/*
+ * search callback possible return status
+ *
+ * DVBFE_ALGO_SEARCH_SUCCESS
+ * The frontend search algorithm completed and returned succesfully
+ *
+ * DVBFE_ALGO_SEARCH_ASLEEP
+ * The frontend search algorithm is sleeping
+ *
+ * DVBFE_ALGO_SEARCH_FAILED
+ * The frontend search for a signal failed
+ *
+ * DVBFE_ALGO_SEARCH_INVALID
+ * The frontend search algorith was probably supplied with invalid
+ * parameters and the search is an invalid one
+ *
+ * DVBFE_ALGO_SEARCH_ERROR
+ * The frontend search algorithm failed due to some error
+ *
+ * DVBFE_ALGO_SEARCH_AGAIN
+ * The frontend search algorithm was requested to search again
+ */
+enum dvbfe_search {
+ DVBFE_ALGO_SEARCH_SUCCESS = (1 << 0),
+ DVBFE_ALGO_SEARCH_ASLEEP = (1 << 1),
+ DVBFE_ALGO_SEARCH_FAILED = (1 << 2),
+ DVBFE_ALGO_SEARCH_INVALID = (1 << 3),
+ DVBFE_ALGO_SEARCH_AGAIN = (1 << 4),
+ DVBFE_ALGO_SEARCH_ERROR = (1 << 31),
+};
+
+
struct dvb_tuner_ops {
struct dvb_tuner_info info;
@@ -99,6 +218,13 @@ struct dvb_tuner_ops {
* tuners which require sophisticated tuning loops, controlling each parameter seperately. */
int (*set_frequency)(struct dvb_frontend *fe, u32 frequency);
int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
+
+ /*
+ * These are provided seperately from set_params in order to facilitate silicon
+ * tuners which require sophisticated tuning loops, controlling each parameter seperately.
+ */
+ int (*set_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state);
+ int (*get_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state);
};
struct analog_demod_info {
@@ -142,7 +268,7 @@ struct dvb_frontend_ops {
unsigned int *delay,
fe_status_t *status);
/* get frontend tuning algorithm from the module */
- int (*get_frontend_algo)(struct dvb_frontend *fe);
+ enum dvbfe_algo (*get_frontend_algo)(struct dvb_frontend *fe);
/* these two are only used for the swzigzag code */
int (*set_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
@@ -167,6 +293,12 @@ struct dvb_frontend_ops {
int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable);
int (*ts_bus_ctrl)(struct dvb_frontend* fe, int acquire);
+ /* These callbacks are for devices that implement their own
+ * tuning algorithms, rather than a simple swzigzag
+ */
+ enum dvbfe_search (*search)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p);
+ int (*track)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p);
+
struct dvb_tuner_ops tuner_ops;
struct analog_demod_ops analog_ops;
diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c
index a113744a56cc..6c571d9f011c 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.c
+++ b/drivers/media/dvb/dvb-core/dvbdev.c
@@ -50,33 +50,27 @@ static const char * const dnames[] = {
"net", "osd"
};
+#ifdef CONFIG_DVB_DYNAMIC_MINORS
+#define MAX_DVB_MINORS 256
+#define DVB_MAX_IDS MAX_DVB_MINORS
+#else
#define DVB_MAX_IDS 4
#define nums2minor(num,type,id) ((num << 6) | (id << 4) | type)
#define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64)
+#endif
static struct class *dvb_class;
-static struct dvb_device* dvbdev_find_device (int minor)
-{
- struct dvb_adapter *adap;
-
- list_for_each_entry(adap, &dvb_adapter_list, list_head) {
- struct dvb_device *dev;
- list_for_each_entry(dev, &adap->device_list, list_head)
- if (nums2minor(adap->num, dev->type, dev->id) == minor)
- return dev;
- }
-
- return NULL;
-}
-
+static struct dvb_device *dvb_minors[MAX_DVB_MINORS];
+static DECLARE_RWSEM(minor_rwsem);
static int dvb_device_open(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev;
lock_kernel();
- dvbdev = dvbdev_find_device (iminor(inode));
+ down_read(&minor_rwsem);
+ dvbdev = dvb_minors[iminor(inode)];
if (dvbdev && dvbdev->fops) {
int err = 0;
@@ -92,9 +86,11 @@ static int dvb_device_open(struct inode *inode, struct file *file)
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
+ up_read(&minor_rwsem);
unlock_kernel();
return err;
}
+ up_read(&minor_rwsem);
unlock_kernel();
return -ENODEV;
}
@@ -192,6 +188,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
struct dvb_device *dvbdev;
struct file_operations *dvbdevfops;
struct device *clsdev;
+ int minor;
int id;
mutex_lock(&dvbdev_register_lock);
@@ -231,11 +228,31 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
list_add_tail (&dvbdev->list_head, &adap->device_list);
+ down_write(&minor_rwsem);
+#ifdef CONFIG_DVB_DYNAMIC_MINORS
+ for (minor = 0; minor < MAX_DVB_MINORS; minor++)
+ if (dvb_minors[minor] == NULL)
+ break;
+
+ if (minor == MAX_DVB_MINORS) {
+ kfree(dvbdevfops);
+ kfree(dvbdev);
+ mutex_unlock(&dvbdev_register_lock);
+ return -EINVAL;
+ }
+#else
+ minor = nums2minor(adap->num, type, id);
+#endif
+
+ dvbdev->minor = minor;
+ dvb_minors[minor] = dvbdev;
+ up_write(&minor_rwsem);
+
mutex_unlock(&dvbdev_register_lock);
clsdev = device_create(dvb_class, adap->device,
- MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)),
- NULL, "dvb%d.%s%d", adap->num, dnames[type], id);
+ MKDEV(DVB_MAJOR, minor),
+ dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id);
if (IS_ERR(clsdev)) {
printk(KERN_ERR "%s: failed to create device dvb%d.%s%d (%ld)\n",
__func__, adap->num, dnames[type], id, PTR_ERR(clsdev));
@@ -243,8 +260,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
}
dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
- adap->num, dnames[type], id, nums2minor(adap->num, type, id),
- nums2minor(adap->num, type, id));
+ adap->num, dnames[type], id, minor, minor);
return 0;
}
@@ -256,8 +272,11 @@ void dvb_unregister_device(struct dvb_device *dvbdev)
if (!dvbdev)
return;
- device_destroy(dvb_class, MKDEV(DVB_MAJOR, nums2minor(dvbdev->adapter->num,
- dvbdev->type, dvbdev->id)));
+ down_write(&minor_rwsem);
+ dvb_minors[dvbdev->minor] = NULL;
+ up_write(&minor_rwsem);
+
+ device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
list_del (&dvbdev->list_head);
kfree (dvbdev->fops);
@@ -413,6 +432,15 @@ out:
return err;
}
+static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct dvb_device *dvbdev = dev_get_drvdata(dev);
+
+ add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id);
+ add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num);
+ return 0;
+}
+
static int __init init_dvbdev(void)
{
int retval;
@@ -434,6 +462,7 @@ static int __init init_dvbdev(void)
retval = PTR_ERR(dvb_class);
goto error;
}
+ dvb_class->dev_uevent = dvb_uevent;
return 0;
error:
diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h
index 574e336bac35..dca49cf962e8 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.h
+++ b/drivers/media/dvb/dvb-core/dvbdev.h
@@ -74,6 +74,7 @@ struct dvb_device {
struct file_operations *fops;
struct dvb_adapter *adapter;
int type;
+ int minor;
u32 id;
/* in theory, 'users' can vanish now,
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
index e9ab0249d133..e1e9aa5c6b84 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -733,9 +733,19 @@ static int af9015_read_config(struct usb_device *udev)
af9015_config.ir_table_size =
ARRAY_SIZE(af9015_ir_table_mygictv);
break;
+ case AF9015_REMOTE_DIGITTRADE_DVB_T:
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_digittrade;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_digittrade);
+ af9015_config.ir_table =
+ af9015_ir_table_digittrade;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_digittrade);
+ break;
}
} else {
- switch (udev->descriptor.idVendor) {
+ switch (le16_to_cpu(udev->descriptor.idVendor)) {
case USB_VID_LEADTEK:
af9015_properties[i].rc_key_map =
af9015_rc_keys_leadtek;
@@ -748,7 +758,7 @@ static int af9015_read_config(struct usb_device *udev)
break;
case USB_VID_VISIONPLUS:
if (udev->descriptor.idProduct ==
- USB_PID_AZUREWAVE_AD_TU700) {
+ cpu_to_le16(USB_PID_AZUREWAVE_AD_TU700)) {
af9015_properties[i].rc_key_map =
af9015_rc_keys_twinhan;
af9015_properties[i].rc_key_map_size =
@@ -800,6 +810,16 @@ static int af9015_read_config(struct usb_device *udev)
ARRAY_SIZE(af9015_ir_table_msi);
}
break;
+ case USB_VID_AVERMEDIA:
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_avermedia;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_avermedia);
+ af9015_config.ir_table =
+ af9015_ir_table_avermedia;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_avermedia);
+ break;
}
}
}
@@ -1191,6 +1211,7 @@ static struct usb_device_id af9015_usb_table[] = {
{USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)},
{USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)},
/* 15 */{USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)},
+ {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)},
{0},
};
MODULE_DEVICE_TABLE(usb, af9015_usb_table);
@@ -1343,7 +1364,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.i2c_algo = &af9015_i2c_algo,
- .num_device_descs = 6,
+ .num_device_descs = 7,
.devices = {
{
.name = "Xtensions XD-380",
@@ -1375,6 +1396,12 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.cold_ids = {&af9015_usb_table[15], NULL},
.warm_ids = {NULL},
},
+ {
+ .name = "KWorld USB DVB-T TV Stick II " \
+ "(VS-DVB-T 395U)",
+ .cold_ids = {&af9015_usb_table[16], NULL},
+ .warm_ids = {NULL},
+ },
}
}
};
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
index 6c3c97293316..21c7782f4889 100644
--- a/drivers/media/dvb/dvb-usb/af9015.h
+++ b/drivers/media/dvb/dvb-usb/af9015.h
@@ -123,6 +123,7 @@ enum af9015_remote {
AF9015_REMOTE_A_LINK_DTU_M,
AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
AF9015_REMOTE_MYGICTV_U718,
+ AF9015_REMOTE_DIGITTRADE_DVB_T,
};
/* Leadtek WinFast DTV Dongle Gold */
@@ -520,4 +521,143 @@ static u8 af9015_ir_table_kworld[] = {
0x86, 0x6b, 0x23, 0xdc, 0x45, 0x07, 0x00,
};
+/* AverMedia Volar X */
+static struct dvb_usb_rc_key af9015_rc_keys_avermedia[] = {
+ { 0x05, 0x3d, KEY_PROG1 }, /* SOURCE */
+ { 0x05, 0x12, KEY_POWER }, /* POWER */
+ { 0x05, 0x1e, KEY_1 }, /* 1 */
+ { 0x05, 0x1f, KEY_2 }, /* 2 */
+ { 0x05, 0x20, KEY_3 }, /* 3 */
+ { 0x05, 0x21, KEY_4 }, /* 4 */
+ { 0x05, 0x22, KEY_5 }, /* 5 */
+ { 0x05, 0x23, KEY_6 }, /* 6 */
+ { 0x05, 0x24, KEY_7 }, /* 7 */
+ { 0x05, 0x25, KEY_8 }, /* 8 */
+ { 0x05, 0x26, KEY_9 }, /* 9 */
+ { 0x05, 0x3f, KEY_LEFT }, /* L / DISPLAY */
+ { 0x05, 0x27, KEY_0 }, /* 0 */
+ { 0x05, 0x0f, KEY_RIGHT }, /* R / CH RTN */
+ { 0x05, 0x18, KEY_PROG2 }, /* SNAP SHOT */
+ { 0x05, 0x1c, KEY_PROG3 }, /* 16-CH PREV */
+ { 0x05, 0x2d, KEY_VOLUMEDOWN }, /* VOL DOWN */
+ { 0x05, 0x3e, KEY_ZOOM }, /* FULL SCREEN */
+ { 0x05, 0x2e, KEY_VOLUMEUP }, /* VOL UP */
+ { 0x05, 0x10, KEY_MUTE }, /* MUTE */
+ { 0x05, 0x04, KEY_AUDIO }, /* AUDIO */
+ { 0x05, 0x15, KEY_RECORD }, /* RECORD */
+ { 0x05, 0x11, KEY_PLAY }, /* PLAY */
+ { 0x05, 0x16, KEY_STOP }, /* STOP */
+ { 0x05, 0x0c, KEY_PLAYPAUSE }, /* TIMESHIFT / PAUSE */
+ { 0x05, 0x05, KEY_BACK }, /* << / RED */
+ { 0x05, 0x09, KEY_FORWARD }, /* >> / YELLOW */
+ { 0x05, 0x17, KEY_TEXT }, /* TELETEXT */
+ { 0x05, 0x0a, KEY_EPG }, /* EPG */
+ { 0x05, 0x13, KEY_MENU }, /* MENU */
+
+ { 0x05, 0x0e, KEY_CHANNELUP }, /* CH UP */
+ { 0x05, 0x0d, KEY_CHANNELDOWN }, /* CH DOWN */
+ { 0x05, 0x19, KEY_FIRST }, /* |<< / GREEN */
+ { 0x05, 0x08, KEY_LAST }, /* >>| / BLUE */
+};
+
+static u8 af9015_ir_table_avermedia[] = {
+ 0x02, 0xfd, 0x00, 0xff, 0x12, 0x05, 0x00,
+ 0x02, 0xfd, 0x01, 0xfe, 0x3d, 0x05, 0x00,
+ 0x02, 0xfd, 0x03, 0xfc, 0x17, 0x05, 0x00,
+ 0x02, 0xfd, 0x04, 0xfb, 0x0a, 0x05, 0x00,
+ 0x02, 0xfd, 0x05, 0xfa, 0x1e, 0x05, 0x00,
+ 0x02, 0xfd, 0x06, 0xf9, 0x1f, 0x05, 0x00,
+ 0x02, 0xfd, 0x07, 0xf8, 0x20, 0x05, 0x00,
+ 0x02, 0xfd, 0x09, 0xf6, 0x21, 0x05, 0x00,
+ 0x02, 0xfd, 0x0a, 0xf5, 0x22, 0x05, 0x00,
+ 0x02, 0xfd, 0x0b, 0xf4, 0x23, 0x05, 0x00,
+ 0x02, 0xfd, 0x0d, 0xf2, 0x24, 0x05, 0x00,
+ 0x02, 0xfd, 0x0e, 0xf1, 0x25, 0x05, 0x00,
+ 0x02, 0xfd, 0x0f, 0xf0, 0x26, 0x05, 0x00,
+ 0x02, 0xfd, 0x11, 0xee, 0x27, 0x05, 0x00,
+ 0x02, 0xfd, 0x08, 0xf7, 0x04, 0x05, 0x00,
+ 0x02, 0xfd, 0x0c, 0xf3, 0x3e, 0x05, 0x00,
+ 0x02, 0xfd, 0x10, 0xef, 0x1c, 0x05, 0x00,
+ 0x02, 0xfd, 0x12, 0xed, 0x3f, 0x05, 0x00,
+ 0x02, 0xfd, 0x13, 0xec, 0x0f, 0x05, 0x00,
+ 0x02, 0xfd, 0x14, 0xeb, 0x10, 0x05, 0x00,
+ 0x02, 0xfd, 0x15, 0xea, 0x13, 0x05, 0x00,
+ 0x02, 0xfd, 0x17, 0xe8, 0x18, 0x05, 0x00,
+ 0x02, 0xfd, 0x18, 0xe7, 0x11, 0x05, 0x00,
+ 0x02, 0xfd, 0x19, 0xe6, 0x15, 0x05, 0x00,
+ 0x02, 0xfd, 0x1a, 0xe5, 0x0c, 0x05, 0x00,
+ 0x02, 0xfd, 0x1b, 0xe4, 0x16, 0x05, 0x00,
+ 0x02, 0xfd, 0x1c, 0xe3, 0x09, 0x05, 0x00,
+ 0x02, 0xfd, 0x1d, 0xe2, 0x05, 0x05, 0x00,
+ 0x02, 0xfd, 0x1e, 0xe1, 0x2d, 0x05, 0x00,
+ 0x02, 0xfd, 0x1f, 0xe0, 0x2e, 0x05, 0x00,
+ 0x03, 0xfc, 0x00, 0xff, 0x08, 0x05, 0x00,
+ 0x03, 0xfc, 0x01, 0xfe, 0x19, 0x05, 0x00,
+ 0x03, 0xfc, 0x02, 0xfd, 0x0d, 0x05, 0x00,
+ 0x03, 0xfc, 0x03, 0xfc, 0x0e, 0x05, 0x00,
+};
+
+/* Digittrade DVB-T USB Stick */
+static struct dvb_usb_rc_key af9015_rc_keys_digittrade[] = {
+ { 0x01, 0x0f, KEY_LAST }, /* RETURN */
+ { 0x05, 0x17, KEY_TEXT }, /* TELETEXT */
+ { 0x01, 0x08, KEY_EPG }, /* EPG */
+ { 0x05, 0x13, KEY_POWER }, /* POWER */
+ { 0x01, 0x09, KEY_ZOOM }, /* FULLSCREEN */
+ { 0x00, 0x40, KEY_AUDIO }, /* DUAL SOUND */
+ { 0x00, 0x2c, KEY_PRINT }, /* SNAPSHOT */
+ { 0x05, 0x16, KEY_SUBTITLE }, /* SUBTITLE */
+ { 0x00, 0x52, KEY_CHANNELUP }, /* CH Up */
+ { 0x00, 0x51, KEY_CHANNELDOWN },/* Ch Dn */
+ { 0x00, 0x57, KEY_VOLUMEUP }, /* Vol Up */
+ { 0x00, 0x56, KEY_VOLUMEDOWN }, /* Vol Dn */
+ { 0x01, 0x10, KEY_MUTE }, /* MUTE */
+ { 0x00, 0x27, KEY_0 },
+ { 0x00, 0x1e, KEY_1 },
+ { 0x00, 0x1f, KEY_2 },
+ { 0x00, 0x20, KEY_3 },
+ { 0x00, 0x21, KEY_4 },
+ { 0x00, 0x22, KEY_5 },
+ { 0x00, 0x23, KEY_6 },
+ { 0x00, 0x24, KEY_7 },
+ { 0x00, 0x25, KEY_8 },
+ { 0x00, 0x26, KEY_9 },
+ { 0x01, 0x17, KEY_PLAYPAUSE }, /* TIMESHIFT */
+ { 0x01, 0x15, KEY_RECORD }, /* RECORD */
+ { 0x03, 0x13, KEY_PLAY }, /* PLAY */
+ { 0x01, 0x16, KEY_STOP }, /* STOP */
+ { 0x01, 0x13, KEY_PAUSE }, /* PAUSE */
+};
+
+static u8 af9015_ir_table_digittrade[] = {
+ 0x00, 0xff, 0x06, 0xf9, 0x13, 0x05, 0x00,
+ 0x00, 0xff, 0x4d, 0xb2, 0x17, 0x01, 0x00,
+ 0x00, 0xff, 0x1f, 0xe0, 0x2c, 0x00, 0x00,
+ 0x00, 0xff, 0x0a, 0xf5, 0x15, 0x01, 0x00,
+ 0x00, 0xff, 0x0e, 0xf1, 0x16, 0x01, 0x00,
+ 0x00, 0xff, 0x09, 0xf6, 0x09, 0x01, 0x00,
+ 0x00, 0xff, 0x01, 0xfe, 0x08, 0x01, 0x00,
+ 0x00, 0xff, 0x05, 0xfa, 0x10, 0x01, 0x00,
+ 0x00, 0xff, 0x02, 0xfd, 0x56, 0x00, 0x00,
+ 0x00, 0xff, 0x40, 0xbf, 0x57, 0x00, 0x00,
+ 0x00, 0xff, 0x19, 0xe6, 0x52, 0x00, 0x00,
+ 0x00, 0xff, 0x17, 0xe8, 0x51, 0x00, 0x00,
+ 0x00, 0xff, 0x10, 0xef, 0x0f, 0x01, 0x00,
+ 0x00, 0xff, 0x54, 0xab, 0x27, 0x00, 0x00,
+ 0x00, 0xff, 0x1b, 0xe4, 0x1e, 0x00, 0x00,
+ 0x00, 0xff, 0x11, 0xee, 0x1f, 0x00, 0x00,
+ 0x00, 0xff, 0x15, 0xea, 0x20, 0x00, 0x00,
+ 0x00, 0xff, 0x12, 0xed, 0x21, 0x00, 0x00,
+ 0x00, 0xff, 0x16, 0xe9, 0x22, 0x00, 0x00,
+ 0x00, 0xff, 0x4c, 0xb3, 0x23, 0x00, 0x00,
+ 0x00, 0xff, 0x48, 0xb7, 0x24, 0x00, 0x00,
+ 0x00, 0xff, 0x04, 0xfb, 0x25, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0xff, 0x26, 0x00, 0x00,
+ 0x00, 0xff, 0x1e, 0xe1, 0x13, 0x03, 0x00,
+ 0x00, 0xff, 0x1a, 0xe5, 0x13, 0x01, 0x00,
+ 0x00, 0xff, 0x03, 0xfc, 0x17, 0x05, 0x00,
+ 0x00, 0xff, 0x0d, 0xf2, 0x16, 0x05, 0x00,
+ 0x00, 0xff, 0x1d, 0xe2, 0x40, 0x00, 0x00,
+};
+
#endif
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
index cd2edbcaa097..5017f08b14a6 100644
--- a/drivers/media/dvb/dvb-usb/anysee.c
+++ b/drivers/media/dvb/dvb-usb/anysee.c
@@ -153,7 +153,7 @@ static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
- int ret, inc, i = 0;
+ int ret = 0, inc, i = 0;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/dvb/dvb-usb/cinergyT2-core.c
index 3ac9f74e9fbf..80e37a0d0892 100644
--- a/drivers/media/dvb/dvb-usb/cinergyT2-core.c
+++ b/drivers/media/dvb/dvb-usb/cinergyT2-core.c
@@ -32,7 +32,6 @@
/* debug */
int dvb_usb_cinergyt2_debug;
-int disable_remote;
module_param_named(debug, dvb_usb_cinergyt2_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info, xfer=2, rc=4 "
@@ -45,7 +44,7 @@ struct cinergyt2_state {
};
/* We are missing a release hook with usb_device data */
-struct dvb_usb_device *cinergyt2_usb_device;
+static struct dvb_usb_device *cinergyt2_usb_device;
static struct dvb_usb_device_properties cinergyt2_properties;
diff --git a/drivers/media/dvb/dvb-usb/cinergyT2.h b/drivers/media/dvb/dvb-usb/cinergyT2.h
index 11d79eb384c8..84efe03771eb 100644
--- a/drivers/media/dvb/dvb-usb/cinergyT2.h
+++ b/drivers/media/dvb/dvb-usb/cinergyT2.h
@@ -70,11 +70,11 @@ struct dvbt_get_status_msg {
uint8_t bandwidth;
uint16_t tps;
uint8_t flags;
- uint16_t gain;
+ __le16 gain;
uint8_t snr;
- uint32_t viterbi_error_rate;
+ __le32 viterbi_error_rate;
uint32_t rs_error_rate;
- uint32_t uncorrected_block_count;
+ __le32 uncorrected_block_count;
uint8_t lock_bits;
uint8_t prev_lock_bits;
} __attribute__((packed));
@@ -82,9 +82,9 @@ struct dvbt_get_status_msg {
struct dvbt_set_parameters_msg {
uint8_t cmd;
- uint32_t freq;
+ __le32 freq;
uint8_t bandwidth;
- uint16_t tps;
+ __le16 tps;
uint8_t flags;
} __attribute__((packed));
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 7380b94b3b36..a4fca3fca5ee 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -96,6 +96,7 @@
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
#define USB_PID_KWORLD_399U 0xe399
+#define USB_PID_KWORLD_395U 0xe396
#define USB_PID_KWORLD_PC160_2T 0xc160
#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c
index 6286fbbe7fb5..c65f273ff313 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/drivers/media/dvb/dvb-usb/dw2102.c
@@ -9,7 +9,6 @@
*
* see Documentation/dvb/README.dvb-usb for more information
*/
-#include <linux/version.h>
#include "dw2102.h"
#include "si21xx.h"
#include "stv0299.h"
@@ -27,6 +26,10 @@
#define USB_PID_DW2104 0x2104
#endif
+#ifndef USB_PID_CINERGY_S
+#define USB_PID_CINERGY_S 0x0064
+#endif
+
#define DW210X_READ_MSG 0
#define DW210X_WRITE_MSG 1
@@ -578,6 +581,7 @@ static struct usb_device_id dw2102_table[] = {
{USB_DEVICE(USB_VID_CYPRESS, 0x2101)},
{USB_DEVICE(USB_VID_CYPRESS, 0x2104)},
{USB_DEVICE(0x9022, 0xd650)},
+ {USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)},
{ }
};
@@ -647,6 +651,7 @@ static int dw2102_load_firmware(struct usb_device *dev,
dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
DW210X_WRITE_MSG);
break;
+ case USB_PID_CINERGY_S:
case USB_PID_DW2102:
dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
DW210X_WRITE_MSG);
@@ -655,7 +660,7 @@ static int dw2102_load_firmware(struct usb_device *dev,
/* check STV0299 frontend */
dw210x_op_rw(dev, 0xb5, 0, 0, &reset16[0], 2,
DW210X_READ_MSG);
- if (reset16[0] == 0xa1) {
+ if ((reset16[0] == 0xa1) || (reset16[0] == 0x80)) {
dw2102_properties.i2c_algo = &dw2102_i2c_algo;
dw2102_properties.adapter->tuner_attach = &dw2102_tuner_attach;
break;
@@ -726,7 +731,7 @@ static struct dvb_usb_device_properties dw2102_properties = {
},
}
},
- .num_device_descs = 2,
+ .num_device_descs = 3,
.devices = {
{"DVBWorld DVB-S 2102 USB2.0",
{&dw2102_table[0], NULL},
@@ -736,6 +741,10 @@ static struct dvb_usb_device_properties dw2102_properties = {
{&dw2102_table[1], NULL},
{NULL},
},
+ {"TerraTec Cinergy S USB",
+ {&dw2102_table[4], NULL},
+ {NULL},
+ },
}
};
diff --git a/drivers/media/dvb/dvb-usb/gp8psk-fe.c b/drivers/media/dvb/dvb-usb/gp8psk-fe.c
index 262a858c3068..20eadf9318e0 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk-fe.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk-fe.c
@@ -25,6 +25,20 @@ struct gp8psk_fe_state {
unsigned long status_check_interval;
};
+static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+ u8 status;
+ gp8psk_usb_in_op(st->d, GET_8PSK_CONFIG, 0, 0, &status, 1);
+ return status & bmDCtuned;
+}
+
+static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode)
+{
+ struct gp8psk_fe_state *state = fe->demodulator_priv;
+ return gp8psk_usb_out_op(state->d, SET_8PSK_CONFIG, mode, 0, NULL, 0);
+}
+
static int gp8psk_fe_update_status(struct gp8psk_fe_state *st)
{
u8 buf[6];
@@ -99,39 +113,114 @@ static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_front
return 0;
}
+static int gp8psk_fe_set_property(struct dvb_frontend *fe,
+ struct dtv_property *tvp)
+{
+ deb_fe("%s(..)\n", __func__);
+ return 0;
+}
+
+static int gp8psk_fe_get_property(struct dvb_frontend *fe,
+ struct dtv_property *tvp)
+{
+ deb_fe("%s(..)\n", __func__);
+ return 0;
+}
+
+
static int gp8psk_fe_set_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct gp8psk_fe_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
u8 cmd[10];
u32 freq = fep->frequency * 1000;
+ int gp_product_id = le16_to_cpu(state->d->udev->descriptor.idProduct);
+
+ deb_fe("%s()\n", __func__);
cmd[4] = freq & 0xff;
cmd[5] = (freq >> 8) & 0xff;
cmd[6] = (freq >> 16) & 0xff;
cmd[7] = (freq >> 24) & 0xff;
- switch(fe->ops.info.type) {
- case FE_QPSK:
- cmd[0] = fep->u.qpsk.symbol_rate & 0xff;
- cmd[1] = (fep->u.qpsk.symbol_rate >> 8) & 0xff;
- cmd[2] = (fep->u.qpsk.symbol_rate >> 16) & 0xff;
- cmd[3] = (fep->u.qpsk.symbol_rate >> 24) & 0xff;
- cmd[8] = ADV_MOD_DVB_QPSK;
- cmd[9] = 0x03; /*ADV_MOD_FEC_XXX*/
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ /* Only QPSK is supported for DVB-S */
+ if (c->modulation != QPSK) {
+ deb_fe("%s: unsupported modulation selected (%d)\n",
+ __func__, c->modulation);
+ return -EOPNOTSUPP;
+ }
+ c->fec_inner = FEC_AUTO;
break;
+ case SYS_DVBS2:
+ deb_fe("%s: DVB-S2 delivery system selected\n", __func__);
+ break;
+
default:
- // other modes are unsuported right now
- cmd[0] = 0;
- cmd[1] = 0;
- cmd[2] = 0;
- cmd[3] = 0;
- cmd[8] = 0;
+ deb_fe("%s: unsupported delivery system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
+ cmd[0] = c->symbol_rate & 0xff;
+ cmd[1] = (c->symbol_rate >> 8) & 0xff;
+ cmd[2] = (c->symbol_rate >> 16) & 0xff;
+ cmd[3] = (c->symbol_rate >> 24) & 0xff;
+ switch (c->modulation) {
+ case QPSK:
+ if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
+ if (gp8psk_tuned_to_DCII(fe))
+ gp8psk_bcm4500_reload(state->d);
+ switch (c->fec_inner) {
+ case FEC_1_2:
+ cmd[9] = 0; break;
+ case FEC_2_3:
+ cmd[9] = 1; break;
+ case FEC_3_4:
+ cmd[9] = 2; break;
+ case FEC_5_6:
+ cmd[9] = 3; break;
+ case FEC_7_8:
+ cmd[9] = 4; break;
+ case FEC_AUTO:
+ cmd[9] = 5; break;
+ default:
+ cmd[9] = 5; break;
+ }
+ cmd[8] = ADV_MOD_DVB_QPSK;
+ break;
+ case PSK_8: /* PSK_8 is for compatibility with DN */
+ cmd[8] = ADV_MOD_TURBO_8PSK;
+ switch (c->fec_inner) {
+ case FEC_2_3:
+ cmd[9] = 0; break;
+ case FEC_3_4:
+ cmd[9] = 1; break;
+ case FEC_3_5:
+ cmd[9] = 2; break;
+ case FEC_5_6:
+ cmd[9] = 3; break;
+ case FEC_8_9:
+ cmd[9] = 4; break;
+ default:
+ cmd[9] = 0; break;
+ }
+ break;
+ case QAM_16: /* QAM_16 is for compatibility with DN */
+ cmd[8] = ADV_MOD_TURBO_16QAM;
cmd[9] = 0;
break;
+ default: /* Unknown modulation */
+ deb_fe("%s: unsupported modulation selected (%d)\n",
+ __func__, c->modulation);
+ return -EOPNOTSUPP;
}
- gp8psk_usb_out_op(state->d,TUNE_8PSK,0,0,cmd,10);
+ if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
+ gp8psk_set_tuner_mode(fe, 0);
+ gp8psk_usb_out_op(state->d, TUNE_8PSK, 0, 0, cmd, 10);
state->lock = 0;
state->next_status_check = jiffies;
@@ -140,13 +229,6 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend* fe,
return 0;
}
-static int gp8psk_fe_get_frontend(struct dvb_frontend* fe,
- struct dvb_frontend_parameters *fep)
-{
- return 0;
-}
-
-
static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe,
struct dvb_diseqc_master_cmd *m)
{
@@ -261,9 +343,13 @@ static struct dvb_frontend_ops gp8psk_fe_ops = {
.symbol_rate_max = 45000000,
.symbol_rate_tolerance = 500, /* ppm */
.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_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ /*
+ * FE_CAN_QAM_16 is for compatibility
+ * (Myth incorrectly detects Turbo-QPSK as plain QAM-16)
+ */
+ FE_CAN_QPSK | FE_CAN_QAM_16
},
.release = gp8psk_fe_release,
@@ -271,8 +357,10 @@ static struct dvb_frontend_ops gp8psk_fe_ops = {
.init = NULL,
.sleep = NULL,
+ .set_property = gp8psk_fe_set_property,
+ .get_property = gp8psk_fe_get_property,
.set_frontend = gp8psk_fe_set_frontend,
- .get_frontend = gp8psk_fe_get_frontend,
+
.get_tune_settings = gp8psk_fe_get_tune_settings,
.read_status = gp8psk_fe_read_status,
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c
index d965a923f391..c1da962cc886 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk.c
@@ -174,6 +174,22 @@ static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff)
return 0;
}
+int gp8psk_bcm4500_reload(struct dvb_usb_device *d)
+{
+ u8 buf;
+ int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct);
+ /* Turn off 8psk power */
+ if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1))
+ return -EINVAL;
+ /* Turn On 8psk power */
+ if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1))
+ return -EINVAL;
+ /* load BCM4500 firmware */
+ if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
+ if (gp8psk_load_bcm4500fw(d))
+ return EINVAL;
+ return 0;
+}
static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.h b/drivers/media/dvb/dvb-usb/gp8psk.h
index e5cd8149c23d..e83a57506cfa 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.h
+++ b/drivers/media/dvb/dvb-usb/gp8psk.h
@@ -92,5 +92,6 @@ extern struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d);
extern int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
extern int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen);
+extern int gp8psk_bcm4500_reload(struct dvb_usb_device *d);
#endif
diff --git a/drivers/media/dvb/dvb-usb/usb-urb.c b/drivers/media/dvb/dvb-usb/usb-urb.c
index da93b9e982c0..9da2cc95ca13 100644
--- a/drivers/media/dvb/dvb-usb/usb-urb.c
+++ b/drivers/media/dvb/dvb-usb/usb-urb.c
@@ -156,7 +156,8 @@ static int usb_bulk_urb_init(struct usb_data_stream *stream)
stream->props.u.bulk.buffersize,
usb_urb_complete, stream);
- stream->urb_list[i]->transfer_flags = 0;
+ stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ stream->urb_list[i]->transfer_dma = stream->dma_addr[i];
stream->urbs_initialized++;
}
return 0;
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 96b93e21a84b..00269560793a 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -12,6 +12,25 @@ config DVB_FE_CUSTOMISE
If unsure say N.
+comment "Multistandard (satellite) frontends"
+ depends on DVB_CORE
+
+config DVB_STB0899
+ tristate "STB0899 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S/S2/DSS Multistandard demodulator. Say Y when you want
+ to support this demodulator based frontends
+
+config DVB_STB6100
+ tristate "STB6100 based tuners"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A Silicon tuner from ST used in conjunction with the STB0899
+ demodulator. Say Y when you want to support this tuner.
+
comment "DVB-S (satellite) frontends"
depends on DVB_CORE
@@ -78,6 +97,13 @@ config DVB_TDA10086
help
A DVB-S tuner module. Say Y when you want to support this frontend.
+config DVB_TDA8261
+ tristate "Philips TDA8261 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
config DVB_VES1X93
tristate "VLSI VES1893 or VES1993 based"
depends on DVB_CORE && I2C
@@ -92,6 +118,14 @@ config DVB_TUNER_ITD1000
help
A DVB-S tuner module. Say Y when you want to support this frontend.
+config DVB_TUNER_CX24113
+ tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+
config DVB_TDA826X
tristate "Philips TDA826X silicon tuner"
depends on DVB_CORE && I2C
@@ -345,6 +379,14 @@ config DVB_LGDT330X
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
to support this frontend.
+config DVB_LGDT3304
+ tristate "LG Electronics LGDT3304"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
config DVB_S5H1409
tristate "Samsung S5H1409 based"
depends on DVB_CORE && I2C
@@ -369,6 +411,17 @@ config DVB_S5H1411
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
to support this frontend.
+comment "ISDB-T (terrestrial) frontends"
+ depends on DVB_CORE
+
+config DVB_S921
+ tristate "Sharp S921 tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module.
+ Say Y when you want to support this frontend.
+
comment "Digital terrestrial only tuners/PLL"
depends on DVB_CORE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index aba79f4a63a7..af7bdf0ad4c7 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -5,8 +5,13 @@
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
EXTRA_CFLAGS += -Idrivers/media/common/tuners/
+s921-objs := s921_module.o s921_core.o
+stb0899-objs = stb0899_drv.o stb0899_algo.o
+
obj-$(CONFIG_DVB_PLL) += dvb-pll.o
obj-$(CONFIG_DVB_STV0299) += stv0299.o
+obj-$(CONFIG_DVB_STB0899) += stb0899.o
+obj-$(CONFIG_DVB_STB6100) += stb6100.o
obj-$(CONFIG_DVB_SP8870) += sp8870.o
obj-$(CONFIG_DVB_CX22700) += cx22700.o
obj-$(CONFIG_DVB_CX24110) += cx24110.o
@@ -35,18 +40,21 @@ obj-$(CONFIG_DVB_OR51132) += or51132.o
obj-$(CONFIG_DVB_BCM3510) += bcm3510.o
obj-$(CONFIG_DVB_S5H1420) += s5h1420.o
obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
+obj-$(CONFIG_DVB_LGDT3304) += lgdt3304.o
obj-$(CONFIG_DVB_CX24123) += cx24123.o
obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
obj-$(CONFIG_DVB_ISL6405) += isl6405.o
obj-$(CONFIG_DVB_ISL6421) += isl6421.o
obj-$(CONFIG_DVB_TDA10086) += tda10086.o
obj-$(CONFIG_DVB_TDA826X) += tda826x.o
+obj-$(CONFIG_DVB_TDA8261) += tda8261.o
obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o
obj-$(CONFIG_DVB_TUA6100) += tua6100.o
obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o
obj-$(CONFIG_DVB_AU8522) += au8522.o
obj-$(CONFIG_DVB_TDA10048) += tda10048.o
+obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o
obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
@@ -55,3 +63,5 @@ obj-$(CONFIG_DVB_CX24116) += cx24116.o
obj-$(CONFIG_DVB_SI21XX) += si21xx.o
obj-$(CONFIG_DVB_STV0288) += stv0288.o
obj-$(CONFIG_DVB_STB6000) += stb6000.o
+obj-$(CONFIG_DVB_S921) += s921.o
+
diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c
index 692b68a9e73b..b2b50fb4cfd3 100644
--- a/drivers/media/dvb/frontends/af9013.c
+++ b/drivers/media/dvb/frontends/af9013.c
@@ -223,12 +223,12 @@ static int af9013_set_coeff(struct af9013_state *state, fe_bandwidth_t bw)
int ret = 0;
u8 i = 0;
u8 buf[24];
- u32 ns_coeff1_2048nu;
- u32 ns_coeff1_8191nu;
- u32 ns_coeff1_8192nu;
- u32 ns_coeff1_8193nu;
- u32 ns_coeff2_2k;
- u32 ns_coeff2_8k;
+ u32 uninitialized_var(ns_coeff1_2048nu);
+ u32 uninitialized_var(ns_coeff1_8191nu);
+ u32 uninitialized_var(ns_coeff1_8192nu);
+ u32 uninitialized_var(ns_coeff1_8193nu);
+ u32 uninitialized_var(ns_coeff2_2k);
+ u32 uninitialized_var(ns_coeff2_8k);
deb_info("%s: adc_clock:%d bw:%d\n", __func__,
state->config.adc_clock, bw);
@@ -1009,7 +1009,7 @@ static int af9013_update_snr(struct dvb_frontend *fe)
int ret;
u8 buf[3], i, len;
u32 quant = 0;
- struct snr_table *snr_table;
+ struct snr_table *uninitialized_var(snr_table);
/* check if quantizer ready (for snr) */
ret = af9013_read_reg_bits(state, 0xd2e1, 3, 1, &buf[0]);
diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb/frontends/cx24113.c
new file mode 100644
index 000000000000..f6e7b0380a5a
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx24113.c
@@ -0,0 +1,616 @@
+/*
+ * Driver for Conexant CX24113/CX24128 Tuner (Satellite)
+ *
+ * Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org>
+ *
+ * Developed for BBTI / Technisat
+ *
+ * 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/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "dvb_frontend.h"
+#include "cx24113.h"
+
+static int debug;
+
+#define info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0)
+#define err(args...) do { printk(KERN_ERR "CX24113: " args); } while (0)
+
+#define dprintk(args...) \
+ do { \
+ if (debug) { \
+ printk(KERN_DEBUG "CX24113: %s: ", __func__); \
+ printk(args); \
+ } \
+ } while (0)
+
+struct cx24113_state {
+ struct i2c_adapter *i2c;
+ const struct cx24113_config *config;
+
+#define REV_CX24113 0x23
+ u8 rev;
+ u8 ver;
+
+ u8 icp_mode:1;
+
+#define ICP_LEVEL1 0
+#define ICP_LEVEL2 1
+#define ICP_LEVEL3 2
+#define ICP_LEVEL4 3
+ u8 icp_man:2;
+ u8 icp_auto_low:2;
+ u8 icp_auto_mlow:2;
+ u8 icp_auto_mhi:2;
+ u8 icp_auto_hi:2;
+ u8 icp_dig;
+
+#define LNA_MIN_GAIN 0
+#define LNA_MID_GAIN 1
+#define LNA_MAX_GAIN 2
+ u8 lna_gain:2;
+
+ u8 acp_on:1;
+
+ u8 vco_mode:2;
+ u8 vco_shift:1;
+#define VCOBANDSEL_6 0x80
+#define VCOBANDSEL_5 0x01
+#define VCOBANDSEL_4 0x02
+#define VCOBANDSEL_3 0x04
+#define VCOBANDSEL_2 0x08
+#define VCOBANDSEL_1 0x10
+ u8 vco_band;
+
+#define VCODIV4 4
+#define VCODIV2 2
+ u8 vcodiv;
+
+ u8 bs_delay:4;
+ u16 bs_freqcnt:13;
+ u16 bs_rdiv;
+ u8 prescaler_mode:1;
+
+ u8 rfvga_bias_ctrl;
+
+ s16 tuner_gain_thres;
+ u8 gain_level;
+
+ u32 frequency;
+
+ u8 refdiv;
+
+ u8 Fwindow_enabled;
+};
+
+static int cx24113_writereg(struct cx24113_state *state, int reg, int data)
+{
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .addr = state->config->i2c_addr,
+ .flags = 0, .buf = buf, .len = 2 };
+ int err = i2c_transfer(state->i2c, &msg, 1);
+ if (err != 1) {
+ printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x,"
+ " data == 0x%02x)\n", __func__, err, reg, data);
+ return err;
+ }
+
+ return 0;
+}
+
+static int cx24113_readreg(struct cx24113_state *state, u8 reg)
+{
+ int ret;
+ u8 b;
+ struct i2c_msg msg[] = {
+ { .addr = state->config->i2c_addr,
+ .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = state->config->i2c_addr,
+ .flags = I2C_M_RD, .buf = &b, .len = 1 }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n",
+ __func__, reg, ret);
+ return ret;
+ }
+
+ return b;
+}
+
+static void cx24113_set_parameters(struct cx24113_state *state)
+{
+ u8 r;
+
+ r = cx24113_readreg(state, 0x10) & 0x82;
+ r |= state->icp_mode;
+ r |= state->icp_man << 4;
+ r |= state->icp_dig << 2;
+ r |= state->prescaler_mode << 5;
+ cx24113_writereg(state, 0x10, r);
+
+ r = (state->icp_auto_low << 0) | (state->icp_auto_mlow << 2)
+ | (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6);
+ cx24113_writereg(state, 0x11, r);
+
+ if (state->rev == REV_CX24113) {
+ r = cx24113_readreg(state, 0x20) & 0xec;
+ r |= state->lna_gain;
+ r |= state->rfvga_bias_ctrl << 4;
+ cx24113_writereg(state, 0x20, r);
+ }
+
+ r = cx24113_readreg(state, 0x12) & 0x03;
+ r |= state->acp_on << 2;
+ r |= state->bs_delay << 4;
+ cx24113_writereg(state, 0x12, r);
+
+ r = cx24113_readreg(state, 0x18) & 0x40;
+ r |= state->vco_shift;
+ if (state->vco_band == VCOBANDSEL_6)
+ r |= (1 << 7);
+ else
+ r |= (state->vco_band << 1);
+ cx24113_writereg(state, 0x18, r);
+
+ r = cx24113_readreg(state, 0x14) & 0x20;
+ r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f);
+ cx24113_writereg(state, 0x14, r);
+ cx24113_writereg(state, 0x15, (state->bs_freqcnt & 0xff));
+
+ cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff);
+ r = (cx24113_readreg(state, 0x17) & 0x0f) |
+ ((state->bs_rdiv & 0x0f) << 4);
+ cx24113_writereg(state, 0x17, r);
+}
+
+#define VGA_0 0x00
+#define VGA_1 0x04
+#define VGA_2 0x02
+#define VGA_3 0x06
+#define VGA_4 0x01
+#define VGA_5 0x05
+#define VGA_6 0x03
+#define VGA_7 0x07
+
+#define RFVGA_0 0x00
+#define RFVGA_1 0x01
+#define RFVGA_2 0x02
+#define RFVGA_3 0x03
+
+static int cx24113_set_gain_settings(struct cx24113_state *state,
+ s16 power_estimation)
+{
+ u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0,
+ vga = cx24113_readreg(state, 0x1f) & 0x3f,
+ rfvga = cx24113_readreg(state, 0x20) & 0xf3;
+ u8 gain_level = power_estimation >= state->tuner_gain_thres;
+
+ dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n",
+ power_estimation, state->tuner_gain_thres,
+ state->gain_level, gain_level);
+
+ if (gain_level == state->gain_level)
+ return 0; /* nothing to be done */
+
+ ampout |= 0xf;
+
+ if (gain_level) {
+ rfvga |= RFVGA_0 << 2;
+ vga |= (VGA_7 << 3) | VGA_7;
+ } else {
+ rfvga |= RFVGA_2 << 2;
+ vga |= (VGA_6 << 3) | VGA_2;
+ }
+ state->gain_level = gain_level;
+
+ cx24113_writereg(state, 0x1d, ampout);
+ cx24113_writereg(state, 0x1f, vga);
+ cx24113_writereg(state, 0x20, rfvga);
+
+ return 1; /* did something */
+}
+
+static int cx24113_set_Fref(struct cx24113_state *state, u8 high)
+{
+ u8 xtal = cx24113_readreg(state, 0x02);
+ if (state->rev == 0x43 && state->vcodiv == VCODIV4)
+ high = 1;
+
+ xtal &= ~0x2;
+ if (high)
+ xtal |= high << 1;
+ return cx24113_writereg(state, 0x02, xtal);
+}
+
+static int cx24113_enable(struct cx24113_state *state, u8 enable)
+{
+ u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable;
+ if (state->rev == REV_CX24113)
+ r21 |= (1 << 1);
+ return cx24113_writereg(state, 0x21, r21);
+}
+
+static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz)
+{
+ u8 r;
+
+ if (bandwidth_khz <= 19000)
+ r = 0x03 << 6;
+ else if (bandwidth_khz <= 25000)
+ r = 0x02 << 6;
+ else
+ r = 0x01 << 6;
+
+ dprintk("bandwidth to be set: %d\n", bandwidth_khz);
+ bandwidth_khz *= 10;
+ bandwidth_khz -= 10000;
+ bandwidth_khz /= 1000;
+ bandwidth_khz += 5;
+ bandwidth_khz /= 10;
+
+ dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz);
+
+ r |= bandwidth_khz & 0x3f;
+
+ return cx24113_writereg(state, 0x1e, r);
+}
+
+static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on)
+{
+ u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7);
+ return cx24113_writereg(state, 0x10, r);
+}
+
+static int cx24113_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct cx24113_state *state = fe->tuner_priv;
+ u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1;
+ if (r)
+ *status |= TUNER_STATUS_LOCKED;
+ dprintk("PLL locked: %d\n", r);
+ return 0;
+}
+
+static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv)
+{
+ if (state->rev == 0x43 && state->vcodiv == VCODIV4)
+ refdiv = 2;
+ return state->refdiv = refdiv;
+}
+
+static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
+{
+ s32 N;
+ s64 F;
+ u8 R, r;
+ u8 vcodiv;
+ u8 factor;
+ s32 freq_hz = state->frequency * 1000;
+
+ if (state->config->xtal_khz < 20000)
+ factor = 1;
+ else
+ factor = 2;
+
+ if (state->rev == REV_CX24113) {
+ if (state->frequency >= 1100000)
+ vcodiv = VCODIV2;
+ else
+ vcodiv = VCODIV4;
+ } else {
+ if (state->frequency >= 1165000)
+ vcodiv = VCODIV2;
+ else
+ vcodiv = VCODIV4;
+ }
+ state->vcodiv = vcodiv;
+
+ dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv);
+ R = 0;
+ do {
+ R = cx24113_set_ref_div(state, R + 1);
+
+ /* calculate tuner PLL settings: */
+ N = (freq_hz / 100 * vcodiv) * R;
+ N /= (state->config->xtal_khz) * factor * 2;
+ N += 5; /* For round up. */
+ N /= 10;
+ N -= 32;
+ } while (N < 6 && R < 3);
+
+ if (N < 6) {
+ err("strange frequency: N < 6\n");
+ return;
+ }
+ F = freq_hz;
+ F *= (u64) (R * vcodiv * 262144);
+ dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
+ do_div(F, state->config->xtal_khz*1000 * factor * 2);
+ dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
+ F -= (N + 32) * 262144;
+
+ dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
+
+ if (state->Fwindow_enabled) {
+ if (F > (262144 / 2 - 1638))
+ F = 262144 / 2 - 1638;
+ if (F < (-262144 / 2 + 1638))
+ F = -262144 / 2 + 1638;
+ if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) {
+ F = 0;
+ r = cx24113_readreg(state, 0x10);
+ cx24113_writereg(state, 0x10, r | (1 << 6));
+ }
+ }
+ dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
+
+ *n = (u16) N;
+ *f = (s32) F;
+}
+
+
+static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r)
+{
+ u8 reg;
+ cx24113_writereg(state, 0x19, (n >> 1) & 0xff);
+
+ reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f);
+ cx24113_writereg(state, 0x1a, reg);
+
+ cx24113_writereg(state, 0x1b, (f >> 3) & 0xff);
+
+ reg = cx24113_readreg(state, 0x1c) & 0x1f;
+ cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5));
+
+ cx24113_set_Fref(state, r - 1);
+}
+
+static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency)
+{
+ u8 r = 1; /* or 2 */
+ u16 n = 6;
+ s32 f = 0;
+
+ r = cx24113_readreg(state, 0x14);
+ cx24113_writereg(state, 0x14, r & 0x3f);
+
+ r = cx24113_readreg(state, 0x10);
+ cx24113_writereg(state, 0x10, r & 0xbf);
+
+ state->frequency = frequency;
+
+ dprintk("tuning to frequency: %d\n", frequency);
+
+ cx24113_calc_pll_nf(state, &n, &f);
+ cx24113_set_nfr(state, n, f, state->refdiv);
+
+ r = cx24113_readreg(state, 0x18) & 0xbf;
+ if (state->vcodiv != VCODIV2)
+ r |= 1 << 6;
+ cx24113_writereg(state, 0x18, r);
+
+ /* The need for this sleep is not clear. But helps in some cases */
+ msleep(5);
+
+ r = cx24113_readreg(state, 0x1c) & 0xef;
+ cx24113_writereg(state, 0x1c, r | (1 << 4));
+ return 0;
+}
+
+static int cx24113_init(struct dvb_frontend *fe)
+{
+ struct cx24113_state *state = fe->tuner_priv;
+ int ret;
+
+ state->tuner_gain_thres = -50;
+ state->gain_level = 255; /* to force a gain-setting initialization */
+ state->icp_mode = 0;
+
+ if (state->config->xtal_khz < 11000) {
+ state->icp_auto_hi = ICP_LEVEL4;
+ state->icp_auto_mhi = ICP_LEVEL4;
+ state->icp_auto_mlow = ICP_LEVEL3;
+ state->icp_auto_low = ICP_LEVEL3;
+ } else {
+ state->icp_auto_hi = ICP_LEVEL4;
+ state->icp_auto_mhi = ICP_LEVEL4;
+ state->icp_auto_mlow = ICP_LEVEL3;
+ state->icp_auto_low = ICP_LEVEL2;
+ }
+
+ state->icp_dig = ICP_LEVEL3;
+ state->icp_man = ICP_LEVEL1;
+ state->acp_on = 1;
+ state->vco_mode = 0;
+ state->vco_shift = 0;
+ state->vco_band = VCOBANDSEL_1;
+ state->bs_delay = 8;
+ state->bs_freqcnt = 0x0fff;
+ state->bs_rdiv = 0x0fff;
+ state->prescaler_mode = 0;
+ state->lna_gain = LNA_MAX_GAIN;
+ state->rfvga_bias_ctrl = 1;
+ state->Fwindow_enabled = 1;
+
+ cx24113_set_Fref(state, 0);
+ cx24113_enable(state, 0x3d);
+ cx24113_set_parameters(state);
+
+ cx24113_set_gain_settings(state, -30);
+
+ cx24113_set_bandwidth(state, 18025);
+ cx24113_set_clk_inversion(state, 1);
+
+ if (state->config->xtal_khz >= 40000)
+ ret = cx24113_writereg(state, 0x02,
+ (cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2));
+ else
+ ret = cx24113_writereg(state, 0x02,
+ (cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2));
+
+ return ret;
+}
+
+static int cx24113_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct cx24113_state *state = fe->tuner_priv;
+ /* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */
+ u32 roll_off = 675;
+ u32 bw;
+
+ bw = ((p->u.qpsk.symbol_rate/100) * roll_off) / 1000;
+ bw += (10000000/100) + 5;
+ bw /= 10;
+ bw += 1000;
+ cx24113_set_bandwidth(state, bw);
+
+ cx24113_set_frequency(state, p->frequency);
+ msleep(5);
+ return cx24113_get_status(fe, &bw);
+}
+
+static s8 cx24113_agc_table[2][10] = {
+ {-54, -41, -35, -30, -25, -21, -16, -10, -6, -2},
+ {-39, -35, -30, -25, -19, -15, -11, -5, 1, 9},
+};
+
+void cx24113_agc_callback(struct dvb_frontend *fe)
+{
+ struct cx24113_state *state = fe->tuner_priv;
+ s16 s, i;
+ if (!fe->ops.read_signal_strength)
+ return;
+
+ do {
+ /* this only works with the current CX24123 implementation */
+ fe->ops.read_signal_strength(fe, (u16 *) &s);
+ s >>= 8;
+ dprintk("signal strength: %d\n", s);
+ for (i = 0; i < sizeof(cx24113_agc_table[0]); i++)
+ if (cx24113_agc_table[state->gain_level][i] > s)
+ break;
+ s = -25 - i*5;
+ } while (cx24113_set_gain_settings(state, s));
+}
+EXPORT_SYMBOL(cx24113_agc_callback);
+
+static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct cx24113_state *state = fe->tuner_priv;
+ *frequency = state->frequency;
+ return 0;
+}
+
+static int cx24113_release(struct dvb_frontend *fe)
+{
+ struct cx24113_state *state = fe->tuner_priv;
+ dprintk("\n");
+ fe->tuner_priv = NULL;
+ kfree(state);
+ return 0;
+}
+
+static const struct dvb_tuner_ops cx24113_tuner_ops = {
+ .info = {
+ .name = "Conexant CX24113",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_step = 125,
+ },
+
+ .release = cx24113_release,
+
+ .init = cx24113_init,
+ .sleep = NULL,
+
+ .set_params = cx24113_set_params,
+ .get_frequency = cx24113_get_frequency,
+ .get_bandwidth = NULL,
+ .get_status = cx24113_get_status,
+};
+
+struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
+ const struct cx24113_config *config, struct i2c_adapter *i2c)
+{
+ /* allocate memory for the internal state */
+ struct cx24113_state *state =
+ kzalloc(sizeof(struct cx24113_state), GFP_KERNEL);
+ int rc;
+ if (state == NULL) {
+ err("Unable to kmalloc\n");
+ goto error;
+ }
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+
+ info("trying to detect myself\n");
+
+ /* making a dummy read, because of some expected troubles
+ * after power on */
+ cx24113_readreg(state, 0x00);
+
+ rc = cx24113_readreg(state, 0x00);
+ if (rc < 0) {
+ info("CX24113 not found.\n");
+ goto error;
+ }
+ state->rev = rc;
+
+ switch (rc) {
+ case 0x43:
+ info("detected CX24113 variant\n");
+ break;
+ case REV_CX24113:
+ info("sucessfully detected\n");
+ break;
+ default:
+ err("unsupported device id: %x\n", state->rev);
+ goto error;
+ }
+ state->ver = cx24113_readreg(state, 0x01);
+ info("version: %x\n", state->ver);
+
+ /* create dvb_frontend */
+ memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = state;
+ return fe;
+
+error:
+ kfree(state);
+
+ return NULL;
+}
+EXPORT_SYMBOL(cx24113_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>");
+MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/dvb/frontends/cx24113.h b/drivers/media/dvb/frontends/cx24113.h
index 5ab3dd11076b..5de0f7ffd8d2 100644
--- a/drivers/media/dvb/frontends/cx24113.h
+++ b/drivers/media/dvb/frontends/cx24113.h
@@ -16,7 +16,7 @@
*
* 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.=
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef CX24113_H
@@ -30,9 +30,13 @@ struct cx24113_config {
u32 xtal_khz;
};
-/* TODO: #if defined(CONFIG_DVB_TUNER_CX24113) || \
- * (defined(CONFIG_DVB_TUNER_CX24113_MODULE) && defined(MODULE)) */
+#if defined(CONFIG_DVB_TUNER_CX24113) || \
+ (defined(CONFIG_DVB_TUNER_CX24113_MODULE) && defined(MODULE))
+extern struct dvb_frontend *cx24113_attach(struct dvb_frontend *,
+ const struct cx24113_config *config, struct i2c_adapter *i2c);
+extern void cx24113_agc_callback(struct dvb_frontend *fe);
+#else
static inline struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
const struct cx24113_config *config, struct i2c_adapter *i2c)
{
@@ -44,5 +48,6 @@ static inline void cx24113_agc_callback(struct dvb_frontend *fe)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
}
+#endif
#endif /* CX24113_H */
diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c
index b144b308a4dd..9b6c89e93f16 100644
--- a/drivers/media/dvb/frontends/cx24116.c
+++ b/drivers/media/dvb/frontends/cx24116.c
@@ -106,7 +106,7 @@ MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
#define CX24116_HAS_SYNCLOCK (0x08)
#define CX24116_HAS_UNKNOWN1 (0x10)
#define CX24116_HAS_UNKNOWN2 (0x20)
-#define CX24116_STATUS_MASK (0x3f)
+#define CX24116_STATUS_MASK (0x0f)
#define CX24116_SIGNAL_MASK (0xc0)
#define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */
@@ -160,6 +160,7 @@ struct cx24116_tuning {
fe_spectral_inversion_t inversion;
fe_code_rate_t fec;
+ fe_delivery_system_t delsys;
fe_modulation_t modulation;
fe_pilot_t pilot;
fe_rolloff_t rolloff;
@@ -411,14 +412,15 @@ struct cx24116_modfec {
};
static int cx24116_lookup_fecmod(struct cx24116_state *state,
- fe_modulation_t m, fe_code_rate_t f)
+ fe_delivery_system_t d, fe_modulation_t m, fe_code_rate_t f)
{
int i, ret = -EOPNOTSUPP;
dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f);
for (i = 0; i < ARRAY_SIZE(CX24116_MODFEC_MODES); i++) {
- if ((m == CX24116_MODFEC_MODES[i].modulation) &&
+ if ((d == CX24116_MODFEC_MODES[i].delivery_system) &&
+ (m == CX24116_MODFEC_MODES[i].modulation) &&
(f == CX24116_MODFEC_MODES[i].fec)) {
ret = i;
break;
@@ -429,13 +431,13 @@ static int cx24116_lookup_fecmod(struct cx24116_state *state,
}
static int cx24116_set_fec(struct cx24116_state *state,
- fe_modulation_t mod, fe_code_rate_t fec)
+ fe_delivery_system_t delsys, fe_modulation_t mod, fe_code_rate_t fec)
{
int ret = 0;
dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec);
- ret = cx24116_lookup_fecmod(state, mod, fec);
+ ret = cx24116_lookup_fecmod(state, delsys, mod, fec);
if (ret < 0)
return ret;
@@ -679,7 +681,8 @@ static int cx24116_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct cx24116_state *state = fe->demodulator_priv;
- int lock = cx24116_readreg(state, CX24116_REG_SSTATUS);
+ int lock = cx24116_readreg(state, CX24116_REG_SSTATUS) &
+ CX24116_STATUS_MASK;
dprintk("%s: status = 0x%02x\n", __func__, lock);
@@ -1205,7 +1208,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe,
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct cx24116_cmd cmd;
fe_status_t tunerstat;
- int i, status, ret, retune;
+ int i, status, ret, retune = 1;
dprintk("%s()\n", __func__);
@@ -1222,7 +1225,6 @@ static int cx24116_set_frontend(struct dvb_frontend *fe,
/* Pilot doesn't exist in DVB-S, turn bit off */
state->dnxt.pilot_val = CX24116_PILOT_OFF;
- retune = 1;
/* DVB-S only supports 0.35 */
if (c->rolloff != ROLLOFF_35) {
@@ -1250,7 +1252,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe,
case PILOT_AUTO: /* Not supported but emulated */
state->dnxt.pilot_val = (c->modulation == QPSK)
? CX24116_PILOT_OFF : CX24116_PILOT_ON;
- retune = 2;
+ retune++;
break;
case PILOT_OFF:
state->dnxt.pilot_val = CX24116_PILOT_OFF;
@@ -1287,6 +1289,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe,
__func__, c->delivery_system);
return -EOPNOTSUPP;
}
+ state->dnxt.delsys = c->delivery_system;
state->dnxt.modulation = c->modulation;
state->dnxt.frequency = c->frequency;
state->dnxt.pilot = c->pilot;
@@ -1297,7 +1300,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe,
return ret;
/* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */
- ret = cx24116_set_fec(state, c->modulation, c->fec_inner);
+ ret = cx24116_set_fec(state, c->delivery_system, c->modulation, c->fec_inner);
if (ret != 0)
return ret;
@@ -1308,6 +1311,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe,
/* discard the 'current' tuning parameters and prepare to tune */
cx24116_clone_params(fe);
+ dprintk("%s: delsys = %d\n", __func__, state->dcur.delsys);
dprintk("%s: modulation = %d\n", __func__, state->dcur.modulation);
dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency);
dprintk("%s: pilot = %d (val = 0x%02x)\n", __func__,
@@ -1427,6 +1431,23 @@ tuned: /* Set/Reset B/W */
return ret;
}
+static int cx24116_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *params,
+ unsigned int mode_flags, unsigned int *delay, fe_status_t *status)
+{
+ *delay = HZ / 5;
+ if (params) {
+ int ret = cx24116_set_frontend(fe, params);
+ if (ret)
+ return ret;
+ }
+ return cx24116_read_status(fe, status);
+}
+
+static int cx24116_get_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
static struct dvb_frontend_ops cx24116_ops = {
.info = {
@@ -1458,6 +1479,8 @@ static struct dvb_frontend_ops cx24116_ops = {
.set_voltage = cx24116_set_voltage,
.diseqc_send_master_cmd = cx24116_send_diseqc_msg,
.diseqc_send_burst = cx24116_diseqc_send_burst,
+ .get_frontend_algo = cx24116_get_algo,
+ .tune = cx24116_tune,
.set_property = cx24116_set_property,
.get_property = cx24116_get_property,
diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h
index 3e8126857127..aab8112e2db2 100644
--- a/drivers/media/dvb/frontends/dib7000p.h
+++ b/drivers/media/dvb/frontends/dib7000p.h
@@ -66,7 +66,8 @@ struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe,
return NULL;
}
-extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
+static inline
+int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
int no_of_demods, u8 default_addr,
struct dib7000p_config cfg[])
{
@@ -74,13 +75,15 @@ extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
return -ENODEV;
}
-extern int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
+static inline
+int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
-extern int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value)
+static inline
+int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
diff --git a/drivers/media/dvb/frontends/drx397xD.c b/drivers/media/dvb/frontends/drx397xD.c
index b9ca5c8d2dd9..ec4e08dbc699 100644
--- a/drivers/media/dvb/frontends/drx397xD.c
+++ b/drivers/media/dvb/frontends/drx397xD.c
@@ -39,7 +39,7 @@ static const char mod_name[] = "drx397xD";
#define F_SET_0D4h 2
enum fw_ix {
-#define _FW_ENTRY(a, b) b
+#define _FW_ENTRY(a, b, c) b
#include "drx397xD_fw.h"
};
@@ -72,11 +72,11 @@ static struct {
int refcnt;
const u8 *data[ARRAY_SIZE(blob_name)];
} fw[] = {
-#define _FW_ENTRY(a, b) { \
- .name = a, \
- .file = 0, \
- .lock = RW_LOCK_UNLOCKED, \
- .refcnt = 0, \
+#define _FW_ENTRY(a, b, c) { \
+ .name = a, \
+ .file = 0, \
+ .lock = __RW_LOCK_UNLOCKED(fw[c].lock), \
+ .refcnt = 0, \
.data = { } }
#include "drx397xD_fw.h"
};
diff --git a/drivers/media/dvb/frontends/drx397xD_fw.h b/drivers/media/dvb/frontends/drx397xD_fw.h
index 01de02a81cd4..c8b44c1e807f 100644
--- a/drivers/media/dvb/frontends/drx397xD_fw.h
+++ b/drivers/media/dvb/frontends/drx397xD_fw.h
@@ -18,8 +18,8 @@
*/
#ifdef _FW_ENTRY
- _FW_ENTRY("drx397xD.A2.fw", DRXD_FW_A2 = 0 ),
- _FW_ENTRY("drx397xD.B1.fw", DRXD_FW_B1 ),
+ _FW_ENTRY("drx397xD.A2.fw", DRXD_FW_A2 = 0, DRXD_FW_A2 ),
+ _FW_ENTRY("drx397xD.B1.fw", DRXD_FW_B1, DRXD_FW_B1 ),
#undef _FW_ENTRY
#endif /* _FW_ENTRY */
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
index ea058153ebfa..9f6349964cda 100644
--- a/drivers/media/dvb/frontends/dvb-pll.c
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -311,7 +311,7 @@ static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = {
.count = 4,
.entries = {
{ 1250000, 500, 0xc4, 0x00},
- { 1550000, 500, 0xc4, 0x40},
+ { 1450000, 500, 0xc4, 0x40},
{ 2050000, 500, 0xc4, 0x80},
{ 2150000, 500, 0xc4, 0xc0},
},
diff --git a/drivers/media/dvb/frontends/lgdt3304.c b/drivers/media/dvb/frontends/lgdt3304.c
new file mode 100644
index 000000000000..469ace5692c6
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgdt3304.c
@@ -0,0 +1,378 @@
+/*
+ * Driver for LG ATSC lgdt3304 driver
+ *
+ * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "dvb_frontend.h"
+#include "lgdt3304.h"
+
+static unsigned int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"lgdt3304 debugging (default off)");
+
+#define dprintk(fmt, args...) if (debug) do {\
+ printk("lgdt3304 debug: " fmt, ##args); } while (0)
+
+struct lgdt3304_state
+{
+ struct dvb_frontend frontend;
+ fe_modulation_t current_modulation;
+ __u32 snr;
+ __u32 current_frequency;
+ __u8 addr;
+ struct i2c_adapter *i2c;
+};
+
+static int i2c_write_demod_bytes (struct dvb_frontend *fe, __u8 *buf, int len)
+{
+ struct lgdt3304_state *state = fe->demodulator_priv;
+ struct i2c_msg i2cmsgs = {
+ .addr = state->addr,
+ .flags = 0,
+ .len = 3,
+ .buf = buf
+ };
+ int i;
+ int err;
+
+ for (i=0; i<len-1; i+=3){
+ if((err = i2c_transfer(state->i2c, &i2cmsgs, 1))<0) {
+ printk("%s i2c_transfer error %d\n", __FUNCTION__, err);
+ if (err < 0)
+ return err;
+ else
+ return -EREMOTEIO;
+ }
+ i2cmsgs.buf += 3;
+ }
+ return 0;
+}
+
+static int lgdt3304_i2c_read_reg(struct dvb_frontend *fe, unsigned int reg)
+{
+ struct lgdt3304_state *state = fe->demodulator_priv;
+ struct i2c_msg i2cmsgs[2];
+ int ret;
+ __u8 buf;
+
+ __u8 regbuf[2] = { reg>>8, reg&0xff };
+
+ i2cmsgs[0].addr = state->addr;
+ i2cmsgs[0].flags = 0;
+ i2cmsgs[0].len = 2;
+ i2cmsgs[0].buf = regbuf;
+
+ i2cmsgs[1].addr = state->addr;
+ i2cmsgs[1].flags = I2C_M_RD;
+ i2cmsgs[1].len = 1;
+ i2cmsgs[1].buf = &buf;
+
+ if((ret = i2c_transfer(state->i2c, i2cmsgs, 2))<0) {
+ printk("%s i2c_transfer error %d\n", __FUNCTION__, ret);
+ return ret;
+ }
+
+ return buf;
+}
+
+static int lgdt3304_i2c_write_reg(struct dvb_frontend *fe, int reg, int val)
+{
+ struct lgdt3304_state *state = fe->demodulator_priv;
+ char buffer[3] = { reg>>8, reg&0xff, val };
+ int ret;
+
+ struct i2c_msg i2cmsgs = {
+ .addr = state->addr,
+ .flags = 0,
+ .len = 3,
+ .buf=buffer
+ };
+ ret = i2c_transfer(state->i2c, &i2cmsgs, 1);
+ if (ret != 1) {
+ printk("%s i2c_transfer error %d\n", __FUNCTION__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int lgdt3304_soft_Reset(struct dvb_frontend *fe)
+{
+ lgdt3304_i2c_write_reg(fe, 0x0002, 0x9a);
+ lgdt3304_i2c_write_reg(fe, 0x0002, 0x9b);
+ mdelay(200);
+ return 0;
+}
+
+static int lgdt3304_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) {
+ int err = 0;
+
+ static __u8 lgdt3304_vsb8_data[] = {
+ /* 16bit , 8bit */
+ /* regs , val */
+ 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x13,
+ 0x00, 0x0d, 0x02,
+ 0x00, 0x0e, 0x02,
+ 0x00, 0x12, 0x32,
+ 0x00, 0x13, 0xc4,
+ 0x01, 0x12, 0x17,
+ 0x01, 0x13, 0x15,
+ 0x01, 0x14, 0x18,
+ 0x01, 0x15, 0xff,
+ 0x01, 0x16, 0x2c,
+ 0x02, 0x14, 0x67,
+ 0x02, 0x24, 0x8d,
+ 0x04, 0x27, 0x12,
+ 0x04, 0x28, 0x4f,
+ 0x03, 0x08, 0x80,
+ 0x03, 0x09, 0x00,
+ 0x03, 0x0d, 0x00,
+ 0x03, 0x0e, 0x1c,
+ 0x03, 0x14, 0xe1,
+ 0x05, 0x0e, 0x5b,
+ };
+
+ /* not yet tested .. */
+ static __u8 lgdt3304_qam64_data[] = {
+ /* 16bit , 8bit */
+ /* regs , val */
+ 0x00, 0x00, 0x18,
+ 0x00, 0x0d, 0x02,
+ //0x00, 0x0e, 0x02,
+ 0x00, 0x12, 0x2a,
+ 0x00, 0x13, 0x00,
+ 0x03, 0x14, 0xe3,
+ 0x03, 0x0e, 0x1c,
+ 0x03, 0x08, 0x66,
+ 0x03, 0x09, 0x66,
+ 0x03, 0x0a, 0x08,
+ 0x03, 0x0b, 0x9b,
+ 0x05, 0x0e, 0x5b,
+ };
+
+
+ /* tested with KWorld a340 */
+ static __u8 lgdt3304_qam256_data[] = {
+ /* 16bit , 8bit */
+ /* regs , val */
+ 0x00, 0x00, 0x01, //0x19,
+ 0x00, 0x12, 0x2a,
+ 0x00, 0x13, 0x80,
+ 0x00, 0x0d, 0x02,
+ 0x03, 0x14, 0xe3,
+
+ 0x03, 0x0e, 0x1c,
+ 0x03, 0x08, 0x66,
+ 0x03, 0x09, 0x66,
+ 0x03, 0x0a, 0x08,
+ 0x03, 0x0b, 0x9b,
+
+ 0x03, 0x0d, 0x14,
+ //0x05, 0x0e, 0x5b,
+ 0x01, 0x06, 0x4a,
+ 0x01, 0x07, 0x3d,
+ 0x01, 0x08, 0x70,
+ 0x01, 0x09, 0xa3,
+
+ 0x05, 0x04, 0xfd,
+
+ 0x00, 0x0d, 0x82,
+
+ 0x05, 0x0e, 0x5b,
+
+ 0x05, 0x0e, 0x5b,
+
+ 0x00, 0x02, 0x9a,
+
+ 0x00, 0x02, 0x9b,
+
+ 0x00, 0x00, 0x01,
+ 0x00, 0x12, 0x2a,
+ 0x00, 0x13, 0x80,
+ 0x00, 0x0d, 0x02,
+ 0x03, 0x14, 0xe3,
+
+ 0x03, 0x0e, 0x1c,
+ 0x03, 0x08, 0x66,
+ 0x03, 0x09, 0x66,
+ 0x03, 0x0a, 0x08,
+ 0x03, 0x0b, 0x9b,
+
+ 0x03, 0x0d, 0x14,
+ 0x01, 0x06, 0x4a,
+ 0x01, 0x07, 0x3d,
+ 0x01, 0x08, 0x70,
+ 0x01, 0x09, 0xa3,
+
+ 0x05, 0x04, 0xfd,
+
+ 0x00, 0x0d, 0x82,
+
+ 0x05, 0x0e, 0x5b,
+ };
+
+ struct lgdt3304_state *state = fe->demodulator_priv;
+ if (state->current_modulation != param->u.vsb.modulation) {
+ switch(param->u.vsb.modulation) {
+ case VSB_8:
+ err = i2c_write_demod_bytes(fe, lgdt3304_vsb8_data,
+ sizeof(lgdt3304_vsb8_data));
+ break;
+ case QAM_64:
+ err = i2c_write_demod_bytes(fe, lgdt3304_qam64_data,
+ sizeof(lgdt3304_qam64_data));
+ break;
+ case QAM_256:
+ err = i2c_write_demod_bytes(fe, lgdt3304_qam256_data,
+ sizeof(lgdt3304_qam256_data));
+ break;
+ default:
+ break;
+ }
+
+ if (err) {
+ printk("%s error setting modulation\n", __FUNCTION__);
+ } else {
+ state->current_modulation = param->u.vsb.modulation;
+ }
+ }
+ state->current_frequency = param->frequency;
+
+ lgdt3304_soft_Reset(fe);
+
+
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe, param);
+
+ return 0;
+}
+
+static int lgdt3304_init(struct dvb_frontend *fe) {
+ return 0;
+}
+
+static int lgdt3304_sleep(struct dvb_frontend *fe) {
+ return 0;
+}
+
+
+static int lgdt3304_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct lgdt3304_state *state = fe->demodulator_priv;
+ int r011d;
+ int qam_lck;
+
+ *status = 0;
+ dprintk("lgdt read status\n");
+
+ r011d = lgdt3304_i2c_read_reg(fe, 0x011d);
+
+ dprintk("%02x\n", r011d);
+
+ switch(state->current_modulation) {
+ case VSB_8:
+ if (r011d & 0x80) {
+ dprintk("VSB Locked\n");
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_LOCK;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_SIGNAL;
+ }
+ break;
+ case QAM_64:
+ case QAM_256:
+ qam_lck = r011d & 0x7;
+ switch(qam_lck) {
+ case 0x0: dprintk("Unlock\n");
+ break;
+ case 0x4: dprintk("1st Lock in acquisition state\n");
+ break;
+ case 0x6: dprintk("2nd Lock in acquisition state\n");
+ break;
+ case 0x7: dprintk("Final Lock in good reception state\n");
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_LOCK;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_SIGNAL;
+ break;
+ }
+ break;
+ default:
+ printk("%s unhandled modulation\n", __FUNCTION__);
+ }
+
+
+ return 0;
+}
+
+static int lgdt3304_read_ber(struct dvb_frontend *fe, __u32 *ber)
+{
+ dprintk("read ber\n");
+ return 0;
+}
+
+static int lgdt3304_read_snr(struct dvb_frontend *fe, __u16 *snr)
+{
+ dprintk("read snr\n");
+ return 0;
+}
+
+static int lgdt3304_read_ucblocks(struct dvb_frontend *fe, __u32 *ucblocks)
+{
+ dprintk("read ucblocks\n");
+ return 0;
+}
+
+static void lgdt3304_release(struct dvb_frontend *fe)
+{
+ struct lgdt3304_state *state = (struct lgdt3304_state *)fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops demod_lgdt3304={
+ .info = {
+ .name = "LG 3304",
+ .type = FE_ATSC,
+ .frequency_min = 54000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 62500,
+ .symbol_rate_min = 5056941,
+ .symbol_rate_max = 10762000,
+ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ },
+ .init = lgdt3304_init,
+ .sleep = lgdt3304_sleep,
+ .set_frontend = lgdt3304_set_parameters,
+ .read_snr = lgdt3304_read_snr,
+ .read_ber = lgdt3304_read_ber,
+ .read_status = lgdt3304_read_status,
+ .read_ucblocks = lgdt3304_read_ucblocks,
+ .release = lgdt3304_release,
+};
+
+struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config,
+ struct i2c_adapter *i2c)
+{
+
+ struct lgdt3304_state *state;
+ state = kzalloc(sizeof(struct lgdt3304_state), GFP_KERNEL);
+ memset(state, 0x0, sizeof(struct lgdt3304_state));
+ state->addr = config->i2c_address;
+ state->i2c = i2c;
+
+ memcpy(&state->frontend.ops, &demod_lgdt3304, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+}
+
+EXPORT_SYMBOL_GPL(lgdt3304_attach);
+MODULE_AUTHOR("Markus Rechberger <mrechberger@empiatech.com>");
+MODULE_DESCRIPTION("LGE LGDT3304 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/lgdt3304.h b/drivers/media/dvb/frontends/lgdt3304.h
new file mode 100644
index 000000000000..fc409fe59acb
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgdt3304.h
@@ -0,0 +1,45 @@
+/*
+ * Driver for DVB-T lgdt3304 demodulator
+ *
+ * Copyright (C) 2008 Markus Rechberger <mrechberger@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#ifndef LGDT3304_H
+#define LGDT3304_H
+
+#include <linux/dvb/frontend.h>
+
+struct lgdt3304_config
+{
+ /* demodulator's I2C address */
+ u8 i2c_address;
+};
+
+#if defined(CONFIG_DVB_LGDT3304) || (defined(CONFIG_DVB_LGDT3304_MODULE) && defined(MODULE))
+extern struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_LGDT */
+
+#endif /* LGDT3304_H */
diff --git a/drivers/media/dvb/frontends/s5h1411.c b/drivers/media/dvb/frontends/s5h1411.c
index 40644aacffcb..66e2dd6d6fe4 100644
--- a/drivers/media/dvb/frontends/s5h1411.c
+++ b/drivers/media/dvb/frontends/s5h1411.c
@@ -874,6 +874,9 @@ struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config,
/* Note: Leaving the I2C gate open here. */
s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 1);
+ /* Put the device into low-power mode until first use */
+ s5h1411_set_powerstate(&state->frontend, 1);
+
return &state->frontend;
error:
diff --git a/drivers/media/dvb/frontends/s921_core.c b/drivers/media/dvb/frontends/s921_core.c
new file mode 100644
index 000000000000..974b52be9aea
--- /dev/null
+++ b/drivers/media/dvb/frontends/s921_core.c
@@ -0,0 +1,216 @@
+/*
+ * Driver for Sharp s921 driver
+ *
+ * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de>
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "s921_core.h"
+
+static int s921_isdb_init(struct s921_isdb_t *dev);
+static int s921_isdb_set_parameters(struct s921_isdb_t *dev, struct s921_isdb_t_transmission_mode_params *params);
+static int s921_isdb_tune(struct s921_isdb_t *dev, struct s921_isdb_t_tune_params *params);
+static int s921_isdb_get_status(struct s921_isdb_t *dev, void *data);
+
+static u8 init_table[]={ 0x01, 0x40, 0x02, 0x00, 0x03, 0x40, 0x04, 0x01,
+ 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00,
+ 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x5a, 0x0c, 0x00,
+ 0x0d, 0x00, 0x0f, 0x00, 0x13, 0x1b, 0x14, 0x80,
+ 0x15, 0x40, 0x17, 0x70, 0x18, 0x01, 0x19, 0x12,
+ 0x1a, 0x01, 0x1b, 0x12, 0x1c, 0xa0, 0x1d, 0x00,
+ 0x1e, 0x0a, 0x1f, 0x08, 0x20, 0x40, 0x21, 0xff,
+ 0x22, 0x4c, 0x23, 0x4e, 0x24, 0x4c, 0x25, 0x00,
+ 0x26, 0x00, 0x27, 0xf4, 0x28, 0x60, 0x29, 0x88,
+ 0x2a, 0x40, 0x2b, 0x40, 0x2c, 0xff, 0x2d, 0x00,
+ 0x2e, 0xff, 0x2f, 0x00, 0x30, 0x20, 0x31, 0x06,
+ 0x32, 0x0c, 0x34, 0x0f, 0x37, 0xfe, 0x38, 0x00,
+ 0x39, 0x63, 0x3a, 0x10, 0x3b, 0x10, 0x47, 0x00,
+ 0x49, 0xe5, 0x4b, 0x00, 0x50, 0xc0, 0x52, 0x20,
+ 0x54, 0x5a, 0x55, 0x5b, 0x56, 0x40, 0x57, 0x70,
+ 0x5c, 0x50, 0x5d, 0x00, 0x62, 0x17, 0x63, 0x2f,
+ 0x64, 0x6f, 0x68, 0x00, 0x69, 0x89, 0x6a, 0x00,
+ 0x6b, 0x00, 0x6c, 0x00, 0x6d, 0x00, 0x6e, 0x00,
+ 0x70, 0x00, 0x71, 0x00, 0x75, 0x00, 0x76, 0x30,
+ 0x77, 0x01, 0xaf, 0x00, 0xb0, 0xa0, 0xb2, 0x3d,
+ 0xb3, 0x25, 0xb4, 0x8b, 0xb5, 0x4b, 0xb6, 0x3f,
+ 0xb7, 0xff, 0xb8, 0xff, 0xb9, 0xfc, 0xba, 0x00,
+ 0xbb, 0x00, 0xbc, 0x00, 0xd0, 0x30, 0xe4, 0x84,
+ 0xf0, 0x48, 0xf1, 0x19, 0xf2, 0x5a, 0xf3, 0x8e,
+ 0xf4, 0x2d, 0xf5, 0x07, 0xf6, 0x5a, 0xf7, 0xba,
+ 0xf8, 0xd7 };
+
+static u8 c_table[]={ 0x58, 0x8a, 0x7b, 0x59, 0x8c, 0x7b, 0x5a, 0x8e, 0x5b,
+ 0x5b, 0x90, 0x5b, 0x5c, 0x92, 0x5b, 0x5d, 0x94, 0x5b,
+ 0x5e, 0x96, 0x5b, 0x5f, 0x98, 0x3b, 0x60, 0x9a, 0x3b,
+ 0x61, 0x9c, 0x3b, 0x62, 0x9e, 0x3b, 0x63, 0xa0, 0x3b,
+ 0x64, 0xa2, 0x1b, 0x65, 0xa4, 0x1b, 0x66, 0xa6, 0x1b,
+ 0x67, 0xa8, 0x1b, 0x68, 0xaa, 0x1b, 0x69, 0xac, 0x1b,
+ 0x6a, 0xae, 0x1b, 0x6b, 0xb0, 0x1b, 0x6c, 0xb2, 0x1b,
+ 0x6d, 0xb4, 0xfb, 0x6e, 0xb6, 0xfb, 0x6f, 0xb8, 0xfb,
+ 0x70, 0xba, 0xfb, 0x71, 0xbc, 0xdb, 0x72, 0xbe, 0xdb,
+ 0x73, 0xc0, 0xdb, 0x74, 0xc2, 0xdb, 0x75, 0xc4, 0xdb,
+ 0x76, 0xc6, 0xdb, 0x77, 0xc8, 0xbb, 0x78, 0xca, 0xbb,
+ 0x79, 0xcc, 0xbb, 0x7a, 0xce, 0xbb, 0x7b, 0xd0, 0xbb,
+ 0x7c, 0xd2, 0xbb, 0x7d, 0xd4, 0xbb, 0x7e, 0xd6, 0xbb,
+ 0x7f, 0xd8, 0xbb, 0x80, 0xda, 0x9b, 0x81, 0xdc, 0x9b,
+ 0x82, 0xde, 0x9b, 0x83, 0xe0, 0x9b, 0x84, 0xe2, 0x9b,
+ 0x85, 0xe4, 0x9b, 0x86, 0xe6, 0x9b, 0x87, 0xe8, 0x9b,
+ 0x88, 0xea, 0x9b, 0x89, 0xec, 0x9b };
+
+int s921_isdb_cmd(struct s921_isdb_t *dev, u32 cmd, void *data) {
+ switch(cmd) {
+ case ISDB_T_CMD_INIT:
+ s921_isdb_init(dev);
+ break;
+ case ISDB_T_CMD_SET_PARAM:
+ s921_isdb_set_parameters(dev, data);
+ break;
+ case ISDB_T_CMD_TUNE:
+ s921_isdb_tune(dev, data);
+ break;
+ case ISDB_T_CMD_GET_STATUS:
+ s921_isdb_get_status(dev, data);
+ break;
+ default:
+ printk("unhandled command\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s921_isdb_init(struct s921_isdb_t *dev) {
+ unsigned int i;
+ unsigned int ret;
+ printk("isdb_init\n");
+ for (i = 0; i < sizeof(init_table); i+=2) {
+ ret = dev->i2c_write(dev->priv_dev, init_table[i], init_table[i+1]);
+ if (ret != 0) {
+ printk("i2c write failed\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int s921_isdb_set_parameters(struct s921_isdb_t *dev, struct s921_isdb_t_transmission_mode_params *params) {
+
+ int ret;
+ /* auto is sufficient for now, lateron this should be reflected in an extra interface */
+
+
+
+ ret = dev->i2c_write(dev->priv_dev, 0xb0, 0xa0); //mod_b2);
+ ret = dev->i2c_write(dev->priv_dev, 0xb2, 0x3d); //mod_b2);
+
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xb3, 0x25); //mod_b3);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xb4, 0x8b); //mod_b4);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xb5, 0x4b); //mod_b5);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xb6, 0x3f); //mod_b6);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xb7, 0x3f); //mod_b7);
+ if (ret < 0)
+ return -EINVAL;
+
+ return E_OK;
+}
+
+static int s921_isdb_tune(struct s921_isdb_t *dev, struct s921_isdb_t_tune_params *params) {
+
+ int ret;
+ int index;
+
+ index = (params->frequency - 473143000)/6000000;
+
+ if (index > 48) {
+ return -EINVAL;
+ }
+
+ dev->i2c_write(dev->priv_dev, 0x47, 0x60);
+
+ ret = dev->i2c_write(dev->priv_dev, 0x68, 0x00);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0x69, 0x89);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf0, 0x48);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf1, 0x19);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf2, c_table[index*3]);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf3, c_table[index*3+1]);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf4, c_table[index*3+2]);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf5, 0xae);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf6, 0xb7);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf7, 0xba);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf8, 0xd7);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0x68, 0x0a);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0x69, 0x09);
+ if (ret < 0)
+ return -EINVAL;
+
+ dev->i2c_write(dev->priv_dev, 0x01, 0x40);
+ return 0;
+}
+
+static int s921_isdb_get_status(struct s921_isdb_t *dev, void *data) {
+ unsigned int *ret = (unsigned int*)data;
+ u8 ifagc_dt;
+ u8 rfagc_dt;
+
+ mdelay(10);
+ ifagc_dt = dev->i2c_read(dev->priv_dev, 0x81);
+ rfagc_dt = dev->i2c_read(dev->priv_dev, 0x82);
+ if (rfagc_dt == 0x40) {
+ *ret = 1;
+ }
+ return 0;
+}
diff --git a/drivers/media/dvb/frontends/s921_core.h b/drivers/media/dvb/frontends/s921_core.h
new file mode 100644
index 000000000000..de2f10a44e72
--- /dev/null
+++ b/drivers/media/dvb/frontends/s921_core.h
@@ -0,0 +1,114 @@
+#ifndef _S921_CORE_H
+#define _S921_CORE_H
+//#define u8 unsigned int
+//#define u32 unsigned int
+
+
+
+//#define EINVAL -1
+#define E_OK 0
+
+struct s921_isdb_t {
+ void *priv_dev;
+ int (*i2c_write)(void *dev, u8 reg, u8 val);
+ int (*i2c_read)(void *dev, u8 reg);
+};
+
+#define ISDB_T_CMD_INIT 0
+#define ISDB_T_CMD_SET_PARAM 1
+#define ISDB_T_CMD_TUNE 2
+#define ISDB_T_CMD_GET_STATUS 3
+
+struct s921_isdb_t_tune_params {
+ u32 frequency;
+};
+
+struct s921_isdb_t_status {
+};
+
+struct s921_isdb_t_transmission_mode_params {
+ u8 mode;
+ u8 layer_a_mode;
+#define ISDB_T_LA_MODE_1 0
+#define ISDB_T_LA_MODE_2 1
+#define ISDB_T_LA_MODE_3 2
+ u8 layer_a_carrier_modulation;
+#define ISDB_T_LA_CM_DQPSK 0
+#define ISDB_T_LA_CM_QPSK 1
+#define ISDB_T_LA_CM_16QAM 2
+#define ISDB_T_LA_CM_64QAM 3
+#define ISDB_T_LA_CM_NOLAYER 4
+ u8 layer_a_code_rate;
+#define ISDB_T_LA_CR_1_2 0
+#define ISDB_T_LA_CR_2_3 1
+#define ISDB_T_LA_CR_3_4 2
+#define ISDB_T_LA_CR_5_6 4
+#define ISDB_T_LA_CR_7_8 8
+#define ISDB_T_LA_CR_NOLAYER 16
+ u8 layer_a_time_interleave;
+#define ISDB_T_LA_TI_0 0
+#define ISDB_T_LA_TI_1 1
+#define ISDB_T_LA_TI_2 2
+#define ISDB_T_LA_TI_4 4
+#define ISDB_T_LA_TI_8 8
+#define ISDB_T_LA_TI_16 16
+#define ISDB_T_LA_TI_32 32
+ u8 layer_a_nseg;
+
+ u8 layer_b_mode;
+#define ISDB_T_LB_MODE_1 0
+#define ISDB_T_LB_MODE_2 1
+#define ISDB_T_LB_MODE_3 2
+ u8 layer_b_carrier_modulation;
+#define ISDB_T_LB_CM_DQPSK 0
+#define ISDB_T_LB_CM_QPSK 1
+#define ISDB_T_LB_CM_16QAM 2
+#define ISDB_T_LB_CM_64QAM 3
+#define ISDB_T_LB_CM_NOLAYER 4
+ u8 layer_b_code_rate;
+#define ISDB_T_LB_CR_1_2 0
+#define ISDB_T_LB_CR_2_3 1
+#define ISDB_T_LB_CR_3_4 2
+#define ISDB_T_LB_CR_5_6 4
+#define ISDB_T_LB_CR_7_8 8
+#define ISDB_T_LB_CR_NOLAYER 16
+ u8 layer_b_time_interleave;
+#define ISDB_T_LB_TI_0 0
+#define ISDB_T_LB_TI_1 1
+#define ISDB_T_LB_TI_2 2
+#define ISDB_T_LB_TI_4 4
+#define ISDB_T_LB_TI_8 8
+#define ISDB_T_LB_TI_16 16
+#define ISDB_T_LB_TI_32 32
+ u8 layer_b_nseg;
+
+ u8 layer_c_mode;
+#define ISDB_T_LC_MODE_1 0
+#define ISDB_T_LC_MODE_2 1
+#define ISDB_T_LC_MODE_3 2
+ u8 layer_c_carrier_modulation;
+#define ISDB_T_LC_CM_DQPSK 0
+#define ISDB_T_LC_CM_QPSK 1
+#define ISDB_T_LC_CM_16QAM 2
+#define ISDB_T_LC_CM_64QAM 3
+#define ISDB_T_LC_CM_NOLAYER 4
+ u8 layer_c_code_rate;
+#define ISDB_T_LC_CR_1_2 0
+#define ISDB_T_LC_CR_2_3 1
+#define ISDB_T_LC_CR_3_4 2
+#define ISDB_T_LC_CR_5_6 4
+#define ISDB_T_LC_CR_7_8 8
+#define ISDB_T_LC_CR_NOLAYER 16
+ u8 layer_c_time_interleave;
+#define ISDB_T_LC_TI_0 0
+#define ISDB_T_LC_TI_1 1
+#define ISDB_T_LC_TI_2 2
+#define ISDB_T_LC_TI_4 4
+#define ISDB_T_LC_TI_8 8
+#define ISDB_T_LC_TI_16 16
+#define ISDB_T_LC_TI_32 32
+ u8 layer_c_nseg;
+};
+
+int s921_isdb_cmd(struct s921_isdb_t *dev, u32 cmd, void *data);
+#endif
diff --git a/drivers/media/dvb/frontends/s921_module.c b/drivers/media/dvb/frontends/s921_module.c
new file mode 100644
index 000000000000..3cbb9cb2cf47
--- /dev/null
+++ b/drivers/media/dvb/frontends/s921_module.c
@@ -0,0 +1,190 @@
+/*
+ * Driver for Sharp s921 driver
+ *
+ * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de>
+ *
+ * All rights reserved.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "dvb_frontend.h"
+#include "s921_module.h"
+#include "s921_core.h"
+
+static unsigned int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"s921 debugging (default off)");
+
+#define dprintk(fmt, args...) if (debug) do {\
+ printk("s921 debug: " fmt, ##args); } while (0)
+
+struct s921_state
+{
+ struct dvb_frontend frontend;
+ fe_modulation_t current_modulation;
+ __u32 snr;
+ __u32 current_frequency;
+ __u8 addr;
+ struct s921_isdb_t dev;
+ struct i2c_adapter *i2c;
+};
+
+static int s921_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) {
+ struct s921_state *state = (struct s921_state *)fe->demodulator_priv;
+ struct s921_isdb_t_transmission_mode_params params;
+ struct s921_isdb_t_tune_params tune_params;
+
+ tune_params.frequency = param->frequency;
+ s921_isdb_cmd(&state->dev, ISDB_T_CMD_SET_PARAM, &params);
+ s921_isdb_cmd(&state->dev, ISDB_T_CMD_TUNE, &tune_params);
+ mdelay(100);
+ return 0;
+}
+
+static int s921_init(struct dvb_frontend *fe) {
+ printk("s921 init\n");
+ return 0;
+}
+
+static int s921_sleep(struct dvb_frontend *fe) {
+ printk("s921 sleep\n");
+ return 0;
+}
+
+static int s921_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct s921_state *state = (struct s921_state *)fe->demodulator_priv;
+ unsigned int ret;
+ mdelay(5);
+ s921_isdb_cmd(&state->dev, ISDB_T_CMD_GET_STATUS, &ret);
+ *status = 0;
+
+ printk("status: %02x\n", ret);
+ if (ret == 1) {
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_LOCK;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_SIGNAL;
+ }
+
+ return 0;
+}
+
+static int s921_read_ber(struct dvb_frontend *fe, __u32 *ber)
+{
+ dprintk("read ber\n");
+ return 0;
+}
+
+static int s921_read_snr(struct dvb_frontend *fe, __u16 *snr)
+{
+ dprintk("read snr\n");
+ return 0;
+}
+
+static int s921_read_ucblocks(struct dvb_frontend *fe, __u32 *ucblocks)
+{
+ dprintk("read ucblocks\n");
+ return 0;
+}
+
+static void s921_release(struct dvb_frontend *fe)
+{
+ struct s921_state *state = (struct s921_state *)fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops demod_s921={
+ .info = {
+ .name = "SHARP S921",
+ .type = FE_OFDM,
+ .frequency_min = 473143000,
+ .frequency_max = 767143000,
+ .frequency_stepsize = 6000000,
+ .frequency_tolerance = 0,
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+ .init = s921_init,
+ .sleep = s921_sleep,
+ .set_frontend = s921_set_parameters,
+ .read_snr = s921_read_snr,
+ .read_ber = s921_read_ber,
+ .read_status = s921_read_status,
+ .read_ucblocks = s921_read_ucblocks,
+ .release = s921_release,
+};
+
+static int s921_write(void *dev, u8 reg, u8 val) {
+ struct s921_state *state = dev;
+ char buf[2]={reg,val};
+ int err;
+ struct i2c_msg i2cmsgs = {
+ .addr = state->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = buf
+ };
+
+ if((err = i2c_transfer(state->i2c, &i2cmsgs, 1))<0) {
+ printk("%s i2c_transfer error %d\n", __FUNCTION__, err);
+ if (err < 0)
+ return err;
+ else
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int s921_read(void *dev, u8 reg) {
+ struct s921_state *state = dev;
+ u8 b1;
+ int ret;
+ struct i2c_msg msg[2] = { { .addr = state->addr,
+ .flags = 0,
+ .buf = &reg, .len = 1 },
+ { .addr = state->addr,
+ .flags = I2C_M_RD,
+ .buf = &b1, .len = 1 } };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret != 2)
+ return ret;
+ return b1;
+}
+
+struct dvb_frontend* s921_attach(const struct s921_config *config,
+ struct i2c_adapter *i2c)
+{
+
+ struct s921_state *state;
+ state = kzalloc(sizeof(struct s921_state), GFP_KERNEL);
+ memset(state, 0x0, sizeof(struct s921_state));
+
+ state->addr = config->i2c_address;
+ state->i2c = i2c;
+ state->dev.i2c_write = &s921_write;
+ state->dev.i2c_read = &s921_read;
+ state->dev.priv_dev = state;
+
+ s921_isdb_cmd(&state->dev, ISDB_T_CMD_INIT, NULL);
+
+ memcpy(&state->frontend.ops, &demod_s921, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+}
+
+EXPORT_SYMBOL_GPL(s921_attach);
+MODULE_AUTHOR("Markus Rechberger <mrechberger@empiatech.com>");
+MODULE_DESCRIPTION("Sharp S921 ISDB-T 1Seg");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/s921_module.h b/drivers/media/dvb/frontends/s921_module.h
new file mode 100644
index 000000000000..78660424ba95
--- /dev/null
+++ b/drivers/media/dvb/frontends/s921_module.h
@@ -0,0 +1,49 @@
+/*
+ * Driver for DVB-T s921 demodulator
+ *
+ * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#ifndef S921_MODULE_H
+#define S921_MODULE_H
+
+#include <linux/dvb/frontend.h>
+#include "s921_core.h"
+
+int s921_isdb_init(struct s921_isdb_t *dev);
+int s921_isdb_cmd(struct s921_isdb_t *dev, u32 cmd, void *data);
+
+struct s921_config
+{
+ /* demodulator's I2C address */
+ u8 i2c_address;
+};
+
+#if defined(CONFIG_DVB_S921) || (defined(CONFIG_DVB_S921_MODULE) && defined(MODULE))
+extern struct dvb_frontend* s921_attach(const struct s921_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend* s921_attach(const struct s921_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_S921 */
+
+#endif /* S921_H */
diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c
index 3ddbe69c45ce..0bd16af8a6cd 100644
--- a/drivers/media/dvb/frontends/si21xx.c
+++ b/drivers/media/dvb/frontends/si21xx.c
@@ -8,7 +8,6 @@
* (at your option) any later version.
*
*/
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/media/dvb/frontends/stb0899_algo.c b/drivers/media/dvb/frontends/stb0899_algo.c
new file mode 100644
index 000000000000..ced9b7ae7d50
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb0899_algo.c
@@ -0,0 +1,1519 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ 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 "stb0899_drv.h"
+#include "stb0899_priv.h"
+#include "stb0899_reg.h"
+
+inline u32 stb0899_do_div(u64 n, u32 d)
+{
+ /* wrap do_div() for ease of use */
+
+ do_div(n, d);
+ return n;
+}
+
+/*
+ * stb0899_calc_srate
+ * Compute symbol rate
+ */
+static u32 stb0899_calc_srate(u32 master_clk, u8 *sfr)
+{
+ u64 tmp;
+
+ /* srate = (SFR * master_clk) >> 20 */
+
+ /* sfr is of size 20 bit, stored with an offset of 4 bit */
+ tmp = (((u32)sfr[0]) << 16) | (((u32)sfr[1]) << 8) | sfr[2];
+ tmp &= ~0xf;
+ tmp *= master_clk;
+ tmp >>= 24;
+
+ return tmp;
+}
+
+/*
+ * stb0899_get_srate
+ * Get the current symbol rate
+ */
+u32 stb0899_get_srate(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ u8 sfr[3];
+
+ stb0899_read_regs(state, STB0899_SFRH, sfr, 3);
+
+ return stb0899_calc_srate(internal->master_clk, sfr);
+}
+
+/*
+ * stb0899_set_srate
+ * Set symbol frequency
+ * MasterClock: master clock frequency (hz)
+ * SymbolRate: symbol rate (bauds)
+ * return symbol frequency
+ */
+static u32 stb0899_set_srate(struct stb0899_state *state, u32 master_clk, u32 srate)
+{
+ u32 tmp;
+ u8 sfr[3];
+
+ dprintk(state->verbose, FE_DEBUG, 1, "-->");
+ /*
+ * in order to have the maximum precision, the symbol rate entered into
+ * the chip is computed as the closest value of the "true value".
+ * In this purpose, the symbol rate value is rounded (1 is added on the bit
+ * below the LSB )
+ *
+ * srate = (SFR * master_clk) >> 20
+ * <=>
+ * SFR = srate << 20 / master_clk
+ *
+ * rounded:
+ * SFR = (srate << 21 + master_clk) / (2 * master_clk)
+ *
+ * stored as 20 bit number with an offset of 4 bit:
+ * sfr = SFR << 4;
+ */
+
+ tmp = stb0899_do_div((((u64)srate) << 21) + master_clk, 2 * master_clk);
+ tmp <<= 4;
+
+ sfr[0] = tmp >> 16;
+ sfr[1] = tmp >> 8;
+ sfr[2] = tmp;
+
+ stb0899_write_regs(state, STB0899_SFRH, sfr, 3);
+
+ return srate;
+}
+
+/*
+ * stb0899_calc_derot_time
+ * Compute the amount of time needed by the derotator to lock
+ * SymbolRate: Symbol rate
+ * return: derotator time constant (ms)
+ */
+static long stb0899_calc_derot_time(long srate)
+{
+ if (srate > 0)
+ return (100000 / (srate / 1000));
+ else
+ return 0;
+}
+
+/*
+ * stb0899_carr_width
+ * Compute the width of the carrier
+ * return: width of carrier (kHz or Mhz)
+ */
+long stb0899_carr_width(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+
+ return (internal->srate + (internal->srate * internal->rolloff) / 100);
+}
+
+/*
+ * stb0899_first_subrange
+ * Compute the first subrange of the search
+ */
+static void stb0899_first_subrange(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_params *params = &state->params;
+ struct stb0899_config *config = state->config;
+
+ int range = 0;
+ u32 bandwidth = 0;
+
+ if (config->tuner_get_bandwidth) {
+ stb0899_i2c_gate_ctrl(&state->frontend, 1);
+ config->tuner_get_bandwidth(&state->frontend, &bandwidth);
+ stb0899_i2c_gate_ctrl(&state->frontend, 0);
+ range = bandwidth - stb0899_carr_width(state) / 2;
+ }
+
+ if (range > 0)
+ internal->sub_range = MIN(internal->srch_range, range);
+ else
+ internal->sub_range = 0;
+
+ internal->freq = params->freq;
+ internal->tuner_offst = 0L;
+ internal->sub_dir = 1;
+}
+
+/*
+ * stb0899_check_tmg
+ * check for timing lock
+ * internal.Ttiming: time to wait for loop lock
+ */
+static enum stb0899_status stb0899_check_tmg(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ int lock;
+ u8 reg;
+ s8 timing;
+
+ msleep(internal->t_derot);
+
+ stb0899_write_reg(state, STB0899_RTF, 0xf2);
+ reg = stb0899_read_reg(state, STB0899_TLIR);
+ lock = STB0899_GETFIELD(TLIR_TMG_LOCK_IND, reg);
+ timing = stb0899_read_reg(state, STB0899_RTF);
+
+ if (lock >= 42) {
+ if ((lock > 48) && (ABS(timing) >= 110)) {
+ internal->status = ANALOGCARRIER;
+ dprintk(state->verbose, FE_DEBUG, 1, "-->ANALOG Carrier !");
+ } else {
+ internal->status = TIMINGOK;
+ dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK !");
+ }
+ } else {
+ internal->status = NOTIMING;
+ dprintk(state->verbose, FE_DEBUG, 1, "-->NO TIMING !");
+ }
+ return internal->status;
+}
+
+/*
+ * stb0899_search_tmg
+ * perform a fs/2 zig-zag to find timing
+ */
+static enum stb0899_status stb0899_search_tmg(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_params *params = &state->params;
+
+ short int derot_step, derot_freq = 0, derot_limit, next_loop = 3;
+ int index = 0;
+ u8 cfr[2];
+
+ internal->status = NOTIMING;
+
+ /* timing loop computation & symbol rate optimisation */
+ derot_limit = (internal->sub_range / 2L) / internal->mclk;
+ derot_step = (params->srate / 2L) / internal->mclk;
+
+ while ((stb0899_check_tmg(state) != TIMINGOK) && next_loop) {
+ index++;
+ derot_freq += index * internal->direction * derot_step; /* next derot zig zag position */
+
+ if (ABS(derot_freq) > derot_limit)
+ next_loop--;
+
+ if (next_loop) {
+ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq));
+ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq));
+ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */
+ }
+ internal->direction = -internal->direction; /* Change zigzag direction */
+ }
+
+ if (internal->status == TIMINGOK) {
+ stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */
+ internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]);
+ dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK ! Derot Freq = %d", internal->derot_freq);
+ }
+
+ return internal->status;
+}
+
+/*
+ * stb0899_check_carrier
+ * Check for carrier found
+ */
+static enum stb0899_status stb0899_check_carrier(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ u8 reg;
+
+ msleep(internal->t_derot); /* wait for derotator ok */
+
+ reg = stb0899_read_reg(state, STB0899_CFD);
+ STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_CFD, reg);
+
+ reg = stb0899_read_reg(state, STB0899_DSTATUS);
+ dprintk(state->verbose, FE_DEBUG, 1, "--------------------> STB0899_DSTATUS=[0x%02x]", reg);
+ if (STB0899_GETFIELD(CARRIER_FOUND, reg)) {
+ internal->status = CARRIEROK;
+ dprintk(state->verbose, FE_DEBUG, 1, "-------------> CARRIEROK !");
+ } else {
+ internal->status = NOCARRIER;
+ dprintk(state->verbose, FE_DEBUG, 1, "-------------> NOCARRIER !");
+ }
+
+ return internal->status;
+}
+
+/*
+ * stb0899_search_carrier
+ * Search for a QPSK carrier with the derotator
+ */
+static enum stb0899_status stb0899_search_carrier(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+
+ short int derot_freq = 0, last_derot_freq = 0, derot_limit, next_loop = 3;
+ int index = 0;
+ u8 cfr[2];
+ u8 reg;
+
+ internal->status = NOCARRIER;
+ derot_limit = (internal->sub_range / 2L) / internal->mclk;
+ derot_freq = internal->derot_freq;
+
+ reg = stb0899_read_reg(state, STB0899_CFD);
+ STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_CFD, reg);
+
+ do {
+ dprintk(state->verbose, FE_DEBUG, 1, "Derot Freq=%d, mclk=%d", derot_freq, internal->mclk);
+ if (stb0899_check_carrier(state) == NOCARRIER) {
+ index++;
+ last_derot_freq = derot_freq;
+ derot_freq += index * internal->direction * internal->derot_step; /* next zig zag derotator position */
+
+ if(ABS(derot_freq) > derot_limit)
+ next_loop--;
+
+ if (next_loop) {
+ reg = stb0899_read_reg(state, STB0899_CFD);
+ STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_CFD, reg);
+
+ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq));
+ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq));
+ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */
+ }
+ }
+
+ internal->direction = -internal->direction; /* Change zigzag direction */
+ } while ((internal->status != CARRIEROK) && next_loop);
+
+ if (internal->status == CARRIEROK) {
+ stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */
+ internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]);
+ dprintk(state->verbose, FE_DEBUG, 1, "----> CARRIER OK !, Derot Freq=%d", internal->derot_freq);
+ } else {
+ internal->derot_freq = last_derot_freq;
+ }
+
+ return internal->status;
+}
+
+/*
+ * stb0899_check_data
+ * Check for data found
+ */
+static enum stb0899_status stb0899_check_data(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_params *params = &state->params;
+
+ int lock = 0, index = 0, dataTime = 500, loop;
+ u8 reg;
+
+ internal->status = NODATA;
+
+ /* RESET FEC */
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESACS, reg, 1);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+ msleep(1);
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESACS, reg, 0);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ if (params->srate <= 2000000)
+ dataTime = 2000;
+ else if (params->srate <= 5000000)
+ dataTime = 1500;
+ else if (params->srate <= 15000000)
+ dataTime = 1000;
+ else
+ dataTime = 500;
+
+ stb0899_write_reg(state, STB0899_DSTATUS2, 0x00); /* force search loop */
+ while (1) {
+ /* WARNING! VIT LOCKED has to be tested before VIT_END_LOOOP */
+ reg = stb0899_read_reg(state, STB0899_VSTATUS);
+ lock = STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg);
+ loop = STB0899_GETFIELD(VSTATUS_END_LOOPVIT, reg);
+
+ if (lock || loop || (index > dataTime))
+ break;
+ index++;
+ }
+
+ if (lock) { /* DATA LOCK indicator */
+ internal->status = DATAOK;
+ dprintk(state->verbose, FE_DEBUG, 1, "-----------------> DATA OK !");
+ }
+
+ return internal->status;
+}
+
+/*
+ * stb0899_search_data
+ * Search for a QPSK carrier with the derotator
+ */
+static enum stb0899_status stb0899_search_data(struct stb0899_state *state)
+{
+ short int derot_freq, derot_step, derot_limit, next_loop = 3;
+ u8 cfr[2];
+ u8 reg;
+ int index = 1;
+
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_params *params = &state->params;
+
+ derot_step = (params->srate / 4L) / internal->mclk;
+ derot_limit = (internal->sub_range / 2L) / internal->mclk;
+ derot_freq = internal->derot_freq;
+
+ do {
+ if ((internal->status != CARRIEROK) || (stb0899_check_data(state) != DATAOK)) {
+
+ derot_freq += index * internal->direction * derot_step; /* next zig zag derotator position */
+ if (ABS(derot_freq) > derot_limit)
+ next_loop--;
+
+ if (next_loop) {
+ dprintk(state->verbose, FE_DEBUG, 1, "Derot freq=%d, mclk=%d", derot_freq, internal->mclk);
+ reg = stb0899_read_reg(state, STB0899_CFD);
+ STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_CFD, reg);
+
+ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq));
+ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq));
+ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */
+
+ stb0899_check_carrier(state);
+ index++;
+ }
+ }
+ internal->direction = -internal->direction; /* change zig zag direction */
+ } while ((internal->status != DATAOK) && next_loop);
+
+ if (internal->status == DATAOK) {
+ stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */
+ internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]);
+ dprintk(state->verbose, FE_DEBUG, 1, "------> DATAOK ! Derot Freq=%d", internal->derot_freq);
+ }
+
+ return internal->status;
+}
+
+/*
+ * stb0899_check_range
+ * check if the found frequency is in the correct range
+ */
+static enum stb0899_status stb0899_check_range(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_params *params = &state->params;
+
+ int range_offst, tp_freq;
+
+ range_offst = internal->srch_range / 2000;
+ tp_freq = internal->freq + (internal->derot_freq * internal->mclk) / 1000;
+
+ if ((tp_freq >= params->freq - range_offst) && (tp_freq <= params->freq + range_offst)) {
+ internal->status = RANGEOK;
+ dprintk(state->verbose, FE_DEBUG, 1, "----> RANGEOK !");
+ } else {
+ internal->status = OUTOFRANGE;
+ dprintk(state->verbose, FE_DEBUG, 1, "----> OUT OF RANGE !");
+ }
+
+ return internal->status;
+}
+
+/*
+ * NextSubRange
+ * Compute the next subrange of the search
+ */
+static void next_sub_range(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_params *params = &state->params;
+
+ long old_sub_range;
+
+ if (internal->sub_dir > 0) {
+ old_sub_range = internal->sub_range;
+ internal->sub_range = MIN((internal->srch_range / 2) -
+ (internal->tuner_offst + internal->sub_range / 2),
+ internal->sub_range);
+
+ if (internal->sub_range < 0)
+ internal->sub_range = 0;
+
+ internal->tuner_offst += (old_sub_range + internal->sub_range) / 2;
+ }
+
+ internal->freq = params->freq + (internal->sub_dir * internal->tuner_offst) / 1000;
+ internal->sub_dir = -internal->sub_dir;
+}
+
+/*
+ * stb0899_dvbs_algo
+ * Search for a signal, timing, carrier and data for a
+ * given frequency in a given range
+ */
+enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state)
+{
+ struct stb0899_params *params = &state->params;
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+
+ u8 bclc, reg;
+ u8 cfr[2];
+ u8 eq_const[10];
+ s32 clnI = 3;
+ u32 bandwidth = 0;
+
+ /* BETA values rated @ 99MHz */
+ s32 betaTab[5][4] = {
+ /* 5 10 20 30MBps */
+ { 37, 34, 32, 31 }, /* QPSK 1/2 */
+ { 37, 35, 33, 31 }, /* QPSK 2/3 */
+ { 37, 35, 33, 31 }, /* QPSK 3/4 */
+ { 37, 36, 33, 32 }, /* QPSK 5/6 */
+ { 37, 36, 33, 32 } /* QPSK 7/8 */
+ };
+
+ internal->direction = 1;
+
+ stb0899_set_srate(state, internal->master_clk, params->srate);
+ /* Carrier loop optimization versus symbol rate for acquisition*/
+ if (params->srate <= 5000000) {
+ stb0899_write_reg(state, STB0899_ACLC, 0x89);
+ bclc = stb0899_read_reg(state, STB0899_BCLC);
+ STB0899_SETFIELD_VAL(BETA, bclc, 0x1c);
+ stb0899_write_reg(state, STB0899_BCLC, bclc);
+ clnI = 0;
+ } else if (params->srate <= 15000000) {
+ stb0899_write_reg(state, STB0899_ACLC, 0xc9);
+ bclc = stb0899_read_reg(state, STB0899_BCLC);
+ STB0899_SETFIELD_VAL(BETA, bclc, 0x22);
+ stb0899_write_reg(state, STB0899_BCLC, bclc);
+ clnI = 1;
+ } else if(params->srate <= 25000000) {
+ stb0899_write_reg(state, STB0899_ACLC, 0x89);
+ bclc = stb0899_read_reg(state, STB0899_BCLC);
+ STB0899_SETFIELD_VAL(BETA, bclc, 0x27);
+ stb0899_write_reg(state, STB0899_BCLC, bclc);
+ clnI = 2;
+ } else {
+ stb0899_write_reg(state, STB0899_ACLC, 0xc8);
+ bclc = stb0899_read_reg(state, STB0899_BCLC);
+ STB0899_SETFIELD_VAL(BETA, bclc, 0x29);
+ stb0899_write_reg(state, STB0899_BCLC, bclc);
+ clnI = 3;
+ }
+
+ dprintk(state->verbose, FE_DEBUG, 1, "Set the timing loop to acquisition");
+ /* Set the timing loop to acquisition */
+ stb0899_write_reg(state, STB0899_RTC, 0x46);
+ stb0899_write_reg(state, STB0899_CFD, 0xee);
+
+ /* !! WARNING !!
+ * Do not read any status variables while acquisition,
+ * If any needed, read before the acquisition starts
+ * querying status while acquiring causes the
+ * acquisition to go bad and hence no locks.
+ */
+ dprintk(state->verbose, FE_DEBUG, 1, "Derot Percent=%d Srate=%d mclk=%d",
+ internal->derot_percent, params->srate, internal->mclk);
+
+ /* Initial calculations */
+ internal->derot_step = internal->derot_percent * (params->srate / 1000L) / internal->mclk; /* DerotStep/1000 * Fsymbol */
+ internal->t_derot = stb0899_calc_derot_time(params->srate);
+ internal->t_data = 500;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "RESET stream merger");
+ /* RESET Stream merger */
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESRS, reg, 1);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ /*
+ * Set KDIVIDER to an intermediate value between
+ * 1/2 and 7/8 for acquisition
+ */
+ reg = stb0899_read_reg(state, STB0899_DEMAPVIT);
+ STB0899_SETFIELD_VAL(DEMAPVIT_KDIVIDER, reg, 60);
+ stb0899_write_reg(state, STB0899_DEMAPVIT, reg);
+
+ stb0899_write_reg(state, STB0899_EQON, 0x01); /* Equalizer OFF while acquiring */
+ stb0899_write_reg(state, STB0899_VITSYNC, 0x19);
+
+ stb0899_first_subrange(state);
+ do {
+ /* Initialisations */
+ cfr[0] = cfr[1] = 0;
+ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* RESET derotator frequency */
+
+ stb0899_write_reg(state, STB0899_RTF, 0);
+ reg = stb0899_read_reg(state, STB0899_CFD);
+ STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_CFD, reg);
+
+ internal->derot_freq = 0;
+ internal->status = NOAGC1;
+
+ /* enable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 1);
+
+ /* Move tuner to frequency */
+ dprintk(state->verbose, FE_DEBUG, 1, "Tuner set frequency");
+ if (state->config->tuner_set_frequency)
+ state->config->tuner_set_frequency(&state->frontend, internal->freq);
+
+ if (state->config->tuner_get_frequency)
+ state->config->tuner_get_frequency(&state->frontend, &internal->freq);
+
+ msleep(internal->t_agc1 + internal->t_agc2 + internal->t_derot); /* AGC1, AGC2 and timing loop */
+ dprintk(state->verbose, FE_DEBUG, 1, "current derot freq=%d", internal->derot_freq);
+ internal->status = AGC1OK;
+
+ /* There is signal in the band */
+ if (config->tuner_get_bandwidth)
+ config->tuner_get_bandwidth(&state->frontend, &bandwidth);
+
+ /* disable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 0);
+
+ if (params->srate <= bandwidth / 2)
+ stb0899_search_tmg(state); /* For low rates (SCPC) */
+ else
+ stb0899_check_tmg(state); /* For high rates (MCPC) */
+
+ if (internal->status == TIMINGOK) {
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "TIMING OK ! Derot freq=%d, mclk=%d",
+ internal->derot_freq, internal->mclk);
+
+ if (stb0899_search_carrier(state) == CARRIEROK) { /* Search for carrier */
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "CARRIER OK ! Derot freq=%d, mclk=%d",
+ internal->derot_freq, internal->mclk);
+
+ if (stb0899_search_data(state) == DATAOK) { /* Check for data */
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "DATA OK ! Derot freq=%d, mclk=%d",
+ internal->derot_freq, internal->mclk);
+
+ if (stb0899_check_range(state) == RANGEOK) {
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "RANGE OK ! derot freq=%d, mclk=%d",
+ internal->derot_freq, internal->mclk);
+
+ internal->freq = params->freq + ((internal->derot_freq * internal->mclk) / 1000);
+ reg = stb0899_read_reg(state, STB0899_PLPARM);
+ internal->fecrate = STB0899_GETFIELD(VITCURPUN, reg);
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "freq=%d, internal resultant freq=%d",
+ params->freq, internal->freq);
+
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "internal puncture rate=%d",
+ internal->fecrate);
+ }
+ }
+ }
+ }
+ if (internal->status != RANGEOK)
+ next_sub_range(state);
+
+ } while (internal->sub_range && internal->status != RANGEOK);
+
+ /* Set the timing loop to tracking */
+ stb0899_write_reg(state, STB0899_RTC, 0x33);
+ stb0899_write_reg(state, STB0899_CFD, 0xf7);
+ /* if locked and range ok, set Kdiv */
+ if (internal->status == RANGEOK) {
+ dprintk(state->verbose, FE_DEBUG, 1, "Locked & Range OK !");
+ stb0899_write_reg(state, STB0899_EQON, 0x41); /* Equalizer OFF while acquiring */
+ stb0899_write_reg(state, STB0899_VITSYNC, 0x39); /* SN to b'11 for acquisition */
+
+ /*
+ * Carrier loop optimization versus
+ * symbol Rate/Puncture Rate for Tracking
+ */
+ reg = stb0899_read_reg(state, STB0899_BCLC);
+ switch (internal->fecrate) {
+ case STB0899_FEC_1_2: /* 13 */
+ stb0899_write_reg(state, STB0899_DEMAPVIT, 0x1a);
+ STB0899_SETFIELD_VAL(BETA, reg, betaTab[0][clnI]);
+ stb0899_write_reg(state, STB0899_BCLC, reg);
+ break;
+ case STB0899_FEC_2_3: /* 18 */
+ stb0899_write_reg(state, STB0899_DEMAPVIT, 44);
+ STB0899_SETFIELD_VAL(BETA, reg, betaTab[1][clnI]);
+ stb0899_write_reg(state, STB0899_BCLC, reg);
+ break;
+ case STB0899_FEC_3_4: /* 21 */
+ stb0899_write_reg(state, STB0899_DEMAPVIT, 60);
+ STB0899_SETFIELD_VAL(BETA, reg, betaTab[2][clnI]);
+ stb0899_write_reg(state, STB0899_BCLC, reg);
+ break;
+ case STB0899_FEC_5_6: /* 24 */
+ stb0899_write_reg(state, STB0899_DEMAPVIT, 75);
+ STB0899_SETFIELD_VAL(BETA, reg, betaTab[3][clnI]);
+ stb0899_write_reg(state, STB0899_BCLC, reg);
+ break;
+ case STB0899_FEC_6_7: /* 25 */
+ stb0899_write_reg(state, STB0899_DEMAPVIT, 88);
+ stb0899_write_reg(state, STB0899_ACLC, 0x88);
+ stb0899_write_reg(state, STB0899_BCLC, 0x9a);
+ break;
+ case STB0899_FEC_7_8: /* 26 */
+ stb0899_write_reg(state, STB0899_DEMAPVIT, 94);
+ STB0899_SETFIELD_VAL(BETA, reg, betaTab[4][clnI]);
+ stb0899_write_reg(state, STB0899_BCLC, reg);
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported Puncture Rate");
+ break;
+ }
+ /* release stream merger RESET */
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESRS, reg, 0);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ /* disable carrier detector */
+ reg = stb0899_read_reg(state, STB0899_CFD);
+ STB0899_SETFIELD_VAL(CFD_ON, reg, 0);
+ stb0899_write_reg(state, STB0899_CFD, reg);
+
+ stb0899_read_regs(state, STB0899_EQUAI1, eq_const, 10);
+ }
+
+ return internal->status;
+}
+
+/*
+ * stb0899_dvbs2_config_uwp
+ * Configure UWP state machine
+ */
+static void stb0899_dvbs2_config_uwp(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+ u32 uwp1, uwp2, uwp3, reg;
+
+ uwp1 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1);
+ uwp2 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL2);
+ uwp3 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL3);
+
+ STB0899_SETFIELD_VAL(UWP_ESN0_AVE, uwp1, config->esno_ave);
+ STB0899_SETFIELD_VAL(UWP_ESN0_QUANT, uwp1, config->esno_quant);
+ STB0899_SETFIELD_VAL(UWP_TH_SOF, uwp1, config->uwp_threshold_sof);
+
+ STB0899_SETFIELD_VAL(FE_COARSE_TRK, uwp2, internal->av_frame_coarse);
+ STB0899_SETFIELD_VAL(FE_FINE_TRK, uwp2, internal->av_frame_fine);
+ STB0899_SETFIELD_VAL(UWP_MISS_TH, uwp2, config->miss_threshold);
+
+ STB0899_SETFIELD_VAL(UWP_TH_ACQ, uwp3, config->uwp_threshold_acq);
+ STB0899_SETFIELD_VAL(UWP_TH_TRACK, uwp3, config->uwp_threshold_track);
+
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL1, STB0899_OFF0_UWP_CNTRL1, uwp1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL2, STB0899_OFF0_UWP_CNTRL2, uwp2);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL3, STB0899_OFF0_UWP_CNTRL3, uwp3);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, SOF_SRCH_TO);
+ STB0899_SETFIELD_VAL(SOF_SEARCH_TIMEOUT, reg, config->sof_search_timeout);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_SOF_SRCH_TO, STB0899_OFF0_SOF_SRCH_TO, reg);
+}
+
+/*
+ * stb0899_dvbs2_config_csm_auto
+ * Set CSM to AUTO mode
+ */
+static void stb0899_dvbs2_config_csm_auto(struct stb0899_state *state)
+{
+ u32 reg;
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1);
+ STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, reg, 1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, reg);
+}
+
+long Log2Int(int number)
+{
+ int i;
+
+ i = 0;
+ while ((1 << i) <= ABS(number))
+ i++;
+
+ if (number == 0)
+ i = 1;
+
+ return i - 1;
+}
+
+/*
+ * stb0899_dvbs2_calc_srate
+ * compute BTR_NOM_FREQ for the symbol rate
+ */
+static u32 stb0899_dvbs2_calc_srate(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+
+ u32 dec_ratio, dec_rate, decim, remain, intval, btr_nom_freq;
+ u32 master_clk, srate;
+
+ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate);
+ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio;
+ dec_rate = Log2Int(dec_ratio);
+ decim = 1 << dec_rate;
+ master_clk = internal->master_clk / 1000;
+ srate = internal->srate / 1000;
+
+ if (decim <= 4) {
+ intval = (decim * (1 << (config->btr_nco_bits - 1))) / master_clk;
+ remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk;
+ } else {
+ intval = (1 << (config->btr_nco_bits - 1)) / (master_clk / 100) * decim / 100;
+ remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk;
+ }
+ btr_nom_freq = (intval * srate) + ((remain * srate) / master_clk);
+
+ return btr_nom_freq;
+}
+
+/*
+ * stb0899_dvbs2_calc_dev
+ * compute the correction to be applied to symbol rate
+ */
+static u32 stb0899_dvbs2_calc_dev(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ u32 dec_ratio, correction, master_clk, srate;
+
+ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate);
+ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio;
+
+ master_clk = internal->master_clk / 1000; /* for integer Caculation*/
+ srate = internal->srate / 1000; /* for integer Caculation*/
+ correction = (512 * master_clk) / (2 * dec_ratio * srate);
+
+ return correction;
+}
+
+/*
+ * stb0899_dvbs2_set_srate
+ * Set DVBS2 symbol rate
+ */
+static void stb0899_dvbs2_set_srate(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+
+ u32 dec_ratio, dec_rate, win_sel, decim, f_sym, btr_nom_freq;
+ u32 correction, freq_adj, band_lim, decim_cntrl, reg;
+ u8 anti_alias;
+
+ /*set decimation to 1*/
+ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate);
+ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio;
+ dec_rate = Log2Int(dec_ratio);
+
+ win_sel = 0;
+ if (dec_rate >= 5)
+ win_sel = dec_rate - 4;
+
+ decim = (1 << dec_rate);
+ /* (FSamp/Fsymbol *100) for integer Caculation */
+ f_sym = internal->master_clk / ((decim * internal->srate) / 1000);
+
+ if (f_sym <= 2250) /* don't band limit signal going into btr block*/
+ band_lim = 1;
+ else
+ band_lim = 0; /* band limit signal going into btr block*/
+
+ decim_cntrl = ((win_sel << 3) & 0x18) + ((band_lim << 5) & 0x20) + (dec_rate & 0x7);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DECIM_CNTRL, STB0899_OFF0_DECIM_CNTRL, decim_cntrl);
+
+ if (f_sym <= 3450)
+ anti_alias = 0;
+ else if (f_sym <= 4250)
+ anti_alias = 1;
+ else
+ anti_alias = 2;
+
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ANTI_ALIAS_SEL, STB0899_OFF0_ANTI_ALIAS_SEL, anti_alias);
+ btr_nom_freq = stb0899_dvbs2_calc_srate(state);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_NOM_FREQ, STB0899_OFF0_BTR_NOM_FREQ, btr_nom_freq);
+
+ correction = stb0899_dvbs2_calc_dev(state);
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL);
+ STB0899_SETFIELD_VAL(BTR_FREQ_CORR, reg, correction);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg);
+
+ /* scale UWP+CSM frequency to sample rate*/
+ freq_adj = internal->srate / (internal->master_clk / 4096);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_FREQ_ADJ_SCALE, STB0899_OFF0_FREQ_ADJ_SCALE, freq_adj);
+}
+
+/*
+ * stb0899_dvbs2_set_btr_loopbw
+ * set bit timing loop bandwidth as a percentage of the symbol rate
+ */
+static void stb0899_dvbs2_set_btr_loopbw(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+
+ u32 sym_peak = 23, zeta = 707, loopbw_percent = 60;
+ s32 dec_ratio, dec_rate, k_btr1_rshft, k_btr1, k_btr0_rshft;
+ s32 k_btr0, k_btr2_rshft, k_direct_shift, k_indirect_shift;
+ u32 decim, K, wn, k_direct, k_indirect;
+ u32 reg;
+
+ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate);
+ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio;
+ dec_rate = Log2Int(dec_ratio);
+ decim = (1 << dec_rate);
+
+ sym_peak *= 576000;
+ K = (1 << config->btr_nco_bits) / (internal->master_clk / 1000);
+ K *= (internal->srate / 1000000) * decim; /*k=k 10^-8*/
+
+ if (K != 0) {
+ K = sym_peak / K;
+ wn = (4 * zeta * zeta) + 1000000;
+ wn = (2 * (loopbw_percent * 1000) * 40 * zeta) /wn; /*wn =wn 10^-8*/
+
+ k_indirect = (wn * wn) / K;
+ k_indirect = k_indirect; /*kindirect = kindirect 10^-6*/
+ k_direct = (2 * wn * zeta) / K; /*kDirect = kDirect 10^-2*/
+ k_direct *= 100;
+
+ k_direct_shift = Log2Int(k_direct) - Log2Int(10000) - 2;
+ k_btr1_rshft = (-1 * k_direct_shift) + config->btr_gain_shift_offset;
+ k_btr1 = k_direct / (1 << k_direct_shift);
+ k_btr1 /= 10000;
+
+ k_indirect_shift = Log2Int(k_indirect + 15) - 20 /*- 2*/;
+ k_btr0_rshft = (-1 * k_indirect_shift) + config->btr_gain_shift_offset;
+ k_btr0 = k_indirect * (1 << (-k_indirect_shift));
+ k_btr0 /= 1000000;
+
+ k_btr2_rshft = 0;
+ if (k_btr0_rshft > 15) {
+ k_btr2_rshft = k_btr0_rshft - 15;
+ k_btr0_rshft = 15;
+ }
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_LOOP_GAIN);
+ STB0899_SETFIELD_VAL(KBTR0_RSHFT, reg, k_btr0_rshft);
+ STB0899_SETFIELD_VAL(KBTR0, reg, k_btr0);
+ STB0899_SETFIELD_VAL(KBTR1_RSHFT, reg, k_btr1_rshft);
+ STB0899_SETFIELD_VAL(KBTR1, reg, k_btr1);
+ STB0899_SETFIELD_VAL(KBTR2_RSHFT, reg, k_btr2_rshft);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, reg);
+ } else
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, 0xc4c4f);
+}
+
+/*
+ * stb0899_dvbs2_set_carr_freq
+ * set nominal frequency for carrier search
+ */
+static void stb0899_dvbs2_set_carr_freq(struct stb0899_state *state, s32 carr_freq, u32 master_clk)
+{
+ struct stb0899_config *config = state->config;
+ s32 crl_nom_freq;
+ u32 reg;
+
+ crl_nom_freq = (1 << config->crl_nco_bits) / master_clk;
+ crl_nom_freq *= carr_freq;
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ);
+ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, crl_nom_freq);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg);
+}
+
+/*
+ * stb0899_dvbs2_init_calc
+ * Initialize DVBS2 UWP, CSM, carrier and timing loops
+ */
+static void stb0899_dvbs2_init_calc(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ s32 steps, step_size;
+ u32 range, reg;
+
+ /* config uwp and csm */
+ stb0899_dvbs2_config_uwp(state);
+ stb0899_dvbs2_config_csm_auto(state);
+
+ /* initialize BTR */
+ stb0899_dvbs2_set_srate(state);
+ stb0899_dvbs2_set_btr_loopbw(state);
+
+ if (internal->srate / 1000000 >= 15)
+ step_size = (1 << 17) / 5;
+ else if (internal->srate / 1000000 >= 10)
+ step_size = (1 << 17) / 7;
+ else if (internal->srate / 1000000 >= 5)
+ step_size = (1 << 17) / 10;
+ else
+ step_size = (1 << 17) / 4;
+
+ range = internal->srch_range / 1000000;
+ steps = (10 * range * (1 << 17)) / (step_size * (internal->srate / 1000000));
+ steps = (steps + 6) / 10;
+ steps = (steps == 0) ? 1 : steps;
+ if (steps % 2 == 0)
+ stb0899_dvbs2_set_carr_freq(state, internal->center_freq -
+ (internal->step_size * (internal->srate / 20000000)),
+ (internal->master_clk) / 1000000);
+ else
+ stb0899_dvbs2_set_carr_freq(state, internal->center_freq, (internal->master_clk) / 1000000);
+
+ /*Set Carrier Search params (zigzag, num steps and freq step size*/
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, ACQ_CNTRL2);
+ STB0899_SETFIELD_VAL(ZIGZAG, reg, 1);
+ STB0899_SETFIELD_VAL(NUM_STEPS, reg, steps);
+ STB0899_SETFIELD_VAL(FREQ_STEPSIZE, reg, step_size);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQ_CNTRL2, STB0899_OFF0_ACQ_CNTRL2, reg);
+}
+
+/*
+ * stb0899_dvbs2_btr_init
+ * initialize the timing loop
+ */
+static void stb0899_dvbs2_btr_init(struct stb0899_state *state)
+{
+ u32 reg;
+
+ /* set enable BTR loopback */
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL);
+ STB0899_SETFIELD_VAL(INTRP_PHS_SENSE, reg, 1);
+ STB0899_SETFIELD_VAL(BTR_ERR_ENA, reg, 1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg);
+
+ /* fix btr freq accum at 0 */
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x10000000);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x00000000);
+
+ /* fix btr freq accum at 0 */
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x10000000);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x00000000);
+}
+
+/*
+ * stb0899_dvbs2_reacquire
+ * trigger a DVB-S2 acquisition
+ */
+static void stb0899_dvbs2_reacquire(struct stb0899_state *state)
+{
+ u32 reg = 0;
+
+ /* demod soft reset */
+ STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg);
+
+ /*Reset Timing Loop */
+ stb0899_dvbs2_btr_init(state);
+
+ /* reset Carrier loop */
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, (1 << 30));
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, 0);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_LOOP_GAIN, STB0899_OFF0_CRL_LOOP_GAIN, 0);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, (1 << 30));
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, 0);
+
+ /*release demod soft reset */
+ reg = 0;
+ STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 0);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg);
+
+ /* start acquisition process */
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQUIRE_TRIG, STB0899_OFF0_ACQUIRE_TRIG, 1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_LOCK_LOST, STB0899_OFF0_LOCK_LOST, 0);
+
+ /* equalizer Init */
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 1);
+
+ /*Start equilizer */
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 0);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL);
+ STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0);
+ STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 0);
+ STB0899_SETFIELD_VAL(EQ_DELAY, reg, 0x05);
+ STB0899_SETFIELD_VAL(EQ_ADAPT_MODE, reg, 0x01);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg);
+
+ /* RESET Packet delineator */
+ stb0899_write_reg(state, STB0899_PDELCTRL, 0x4a);
+}
+
+/*
+ * stb0899_dvbs2_get_dmd_status
+ * get DVB-S2 Demod LOCK status
+ */
+static enum stb0899_status stb0899_dvbs2_get_dmd_status(struct stb0899_state *state, int timeout)
+{
+ int time = -10, lock = 0, uwp, csm;
+ u32 reg;
+
+ do {
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STATUS);
+ dprintk(state->verbose, FE_DEBUG, 1, "DMD_STATUS=[0x%02x]", reg);
+ if (STB0899_GETFIELD(IF_AGC_LOCK, reg))
+ dprintk(state->verbose, FE_DEBUG, 1, "------------->IF AGC LOCKED !");
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2);
+ dprintk(state->verbose, FE_DEBUG, 1, "----------->DMD STAT2=[0x%02x]", reg);
+ uwp = STB0899_GETFIELD(UWP_LOCK, reg);
+ csm = STB0899_GETFIELD(CSM_LOCK, reg);
+ if (uwp && csm)
+ lock = 1;
+
+ time += 10;
+ msleep(10);
+
+ } while ((!lock) && (time <= timeout));
+
+ if (lock) {
+ dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 LOCK !");
+ return DVBS2_DEMOD_LOCK;
+ } else {
+ return DVBS2_DEMOD_NOLOCK;
+ }
+}
+
+/*
+ * stb0899_dvbs2_get_data_lock
+ * get FEC status
+ */
+static int stb0899_dvbs2_get_data_lock(struct stb0899_state *state, int timeout)
+{
+ int time = 0, lock = 0;
+ u8 reg;
+
+ while ((!lock) && (time < timeout)) {
+ reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1);
+ dprintk(state->verbose, FE_DEBUG, 1, "---------> CFGPDELSTATUS=[0x%02x]", reg);
+ lock = STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg);
+ time++;
+ }
+
+ return lock;
+}
+
+/*
+ * stb0899_dvbs2_get_fec_status
+ * get DVB-S2 FEC LOCK status
+ */
+static enum stb0899_status stb0899_dvbs2_get_fec_status(struct stb0899_state *state, int timeout)
+{
+ int time = 0, Locked;
+
+ do {
+ Locked = stb0899_dvbs2_get_data_lock(state, 1);
+ time++;
+ msleep(1);
+
+ } while ((!Locked) && (time < timeout));
+
+ if (Locked) {
+ dprintk(state->verbose, FE_DEBUG, 1, "---------->DVB-S2 FEC LOCK !");
+ return DVBS2_FEC_LOCK;
+ } else {
+ return DVBS2_FEC_NOLOCK;
+ }
+}
+
+
+/*
+ * stb0899_dvbs2_init_csm
+ * set parameters for manual mode
+ */
+static void stb0899_dvbs2_init_csm(struct stb0899_state *state, int pilots, enum stb0899_modcod modcod)
+{
+ struct stb0899_internal *internal = &state->internal;
+
+ s32 dvt_tbl = 1, two_pass = 0, agc_gain = 6, agc_shift = 0, loop_shift = 0, phs_diff_thr = 0x80;
+ s32 gamma_acq, gamma_rho_acq, gamma_trk, gamma_rho_trk, lock_count_thr;
+ u32 csm1, csm2, csm3, csm4;
+
+ if (((internal->master_clk / internal->srate) <= 4) && (modcod <= 11) && (pilots == 1)) {
+ switch (modcod) {
+ case STB0899_QPSK_12:
+ gamma_acq = 25;
+ gamma_rho_acq = 2700;
+ gamma_trk = 12;
+ gamma_rho_trk = 180;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_35:
+ gamma_acq = 38;
+ gamma_rho_acq = 7182;
+ gamma_trk = 14;
+ gamma_rho_trk = 308;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_23:
+ gamma_acq = 42;
+ gamma_rho_acq = 9408;
+ gamma_trk = 17;
+ gamma_rho_trk = 476;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_34:
+ gamma_acq = 53;
+ gamma_rho_acq = 16642;
+ gamma_trk = 19;
+ gamma_rho_trk = 646;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_45:
+ gamma_acq = 53;
+ gamma_rho_acq = 17119;
+ gamma_trk = 22;
+ gamma_rho_trk = 880;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_56:
+ gamma_acq = 55;
+ gamma_rho_acq = 19250;
+ gamma_trk = 23;
+ gamma_rho_trk = 989;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_89:
+ gamma_acq = 60;
+ gamma_rho_acq = 24240;
+ gamma_trk = 24;
+ gamma_rho_trk = 1176;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_910:
+ gamma_acq = 66;
+ gamma_rho_acq = 29634;
+ gamma_trk = 24;
+ gamma_rho_trk = 1176;
+ lock_count_thr = 8;
+ break;
+ default:
+ gamma_acq = 66;
+ gamma_rho_acq = 29634;
+ gamma_trk = 24;
+ gamma_rho_trk = 1176;
+ lock_count_thr = 8;
+ break;
+ }
+
+ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1);
+ STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, csm1, 0);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1);
+
+ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1);
+ csm2 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL2);
+ csm3 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL3);
+ csm4 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL4);
+
+ STB0899_SETFIELD_VAL(CSM_DVT_TABLE, csm1, dvt_tbl);
+ STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, two_pass);
+ STB0899_SETFIELD_VAL(CSM_AGC_GAIN, csm1, agc_gain);
+ STB0899_SETFIELD_VAL(CSM_AGC_SHIFT, csm1, agc_shift);
+ STB0899_SETFIELD_VAL(FE_LOOP_SHIFT, csm1, loop_shift);
+ STB0899_SETFIELD_VAL(CSM_GAMMA_ACQ, csm2, gamma_acq);
+ STB0899_SETFIELD_VAL(CSM_GAMMA_RHOACQ, csm2, gamma_rho_acq);
+ STB0899_SETFIELD_VAL(CSM_GAMMA_TRACK, csm3, gamma_trk);
+ STB0899_SETFIELD_VAL(CSM_GAMMA_RHOTRACK, csm3, gamma_rho_trk);
+ STB0899_SETFIELD_VAL(CSM_LOCKCOUNT_THRESH, csm4, lock_count_thr);
+ STB0899_SETFIELD_VAL(CSM_PHASEDIFF_THRESH, csm4, phs_diff_thr);
+
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL2, STB0899_OFF0_CSM_CNTRL2, csm2);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL3, STB0899_OFF0_CSM_CNTRL3, csm3);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL4, STB0899_OFF0_CSM_CNTRL4, csm4);
+ }
+}
+
+/*
+ * stb0899_dvbs2_get_srate
+ * get DVB-S2 Symbol Rate
+ */
+static u32 stb0899_dvbs2_get_srate(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+
+ u32 bTrNomFreq, srate, decimRate, intval1, intval2, reg;
+ int div1, div2, rem1, rem2;
+
+ div1 = config->btr_nco_bits / 2;
+ div2 = config->btr_nco_bits - div1 - 1;
+
+ bTrNomFreq = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_NOM_FREQ);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DECIM_CNTRL);
+ decimRate = STB0899_GETFIELD(DECIM_RATE, reg);
+ decimRate = (1 << decimRate);
+
+ intval1 = internal->master_clk / (1 << div1);
+ intval2 = bTrNomFreq / (1 << div2);
+
+ rem1 = internal->master_clk % (1 << div1);
+ rem2 = bTrNomFreq % (1 << div2);
+ /* only for integer calculation */
+ srate = (intval1 * intval2) + ((intval1 * rem2) / (1 << div2)) + ((intval2 * rem1) / (1 << div1));
+ srate /= decimRate; /*symbrate = (btrnomfreq_register_val*MasterClock)/2^(27+decim_rate_field) */
+
+ return srate;
+}
+
+/*
+ * stb0899_dvbs2_algo
+ * Search for signal, timing, carrier and data for a given
+ * frequency in a given range
+ */
+enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ enum stb0899_modcod modcod;
+
+ s32 offsetfreq, searchTime, FecLockTime, pilots, iqSpectrum;
+ int i = 0;
+ u32 reg, csm1;
+
+ if (internal->srate <= 2000000) {
+ searchTime = 5000; /* 5000 ms max time to lock UWP and CSM, SYMB <= 2Mbs */
+ FecLockTime = 350; /* 350 ms max time to lock FEC, SYMB <= 2Mbs */
+ } else if (internal->srate <= 5000000) {
+ searchTime = 2500; /* 2500 ms max time to lock UWP and CSM, 2Mbs < SYMB <= 5Mbs */
+ FecLockTime = 170; /* 170 ms max time to lock FEC, 2Mbs< SYMB <= 5Mbs */
+ } else if (internal->srate <= 10000000) {
+ searchTime = 1500; /* 1500 ms max time to lock UWP and CSM, 5Mbs <SYMB <= 10Mbs */
+ FecLockTime = 80; /* 80 ms max time to lock FEC, 5Mbs< SYMB <= 10Mbs */
+ } else if (internal->srate <= 15000000) {
+ searchTime = 500; /* 500 ms max time to lock UWP and CSM, 10Mbs <SYMB <= 15Mbs */
+ FecLockTime = 50; /* 50 ms max time to lock FEC, 10Mbs< SYMB <= 15Mbs */
+ } else if (internal->srate <= 20000000) {
+ searchTime = 300; /* 300 ms max time to lock UWP and CSM, 15Mbs < SYMB <= 20Mbs */
+ FecLockTime = 30; /* 50 ms max time to lock FEC, 15Mbs< SYMB <= 20Mbs */
+ } else if (internal->srate <= 25000000) {
+ searchTime = 250; /* 250 ms max time to lock UWP and CSM, 20 Mbs < SYMB <= 25Mbs */
+ FecLockTime = 25; /* 25 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */
+ } else {
+ searchTime = 150; /* 150 ms max time to lock UWP and CSM, SYMB > 25Mbs */
+ FecLockTime = 20; /* 20 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */
+ }
+
+ /* Maintain Stream Merger in reset during acquisition */
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESRS, reg, 1);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ /* enable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 1);
+
+ /* Move tuner to frequency */
+ if (state->config->tuner_set_frequency)
+ state->config->tuner_set_frequency(&state->frontend, internal->freq);
+ if (state->config->tuner_get_frequency)
+ state->config->tuner_get_frequency(&state->frontend, &internal->freq);
+
+ /* disable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 0);
+
+ /* Set IF AGC to acquisition */
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL);
+ STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 4);
+ STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 32);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2);
+ STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 0);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg);
+
+ /* Initialisation */
+ stb0899_dvbs2_init_calc(state);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2);
+ switch (internal->inversion) {
+ case IQ_SWAP_OFF:
+ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 0);
+ break;
+ case IQ_SWAP_ON:
+ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1);
+ break;
+ case IQ_SWAP_AUTO: /* use last successful search first */
+ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1);
+ break;
+ }
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg);
+ stb0899_dvbs2_reacquire(state);
+
+ /* Wait for demod lock (UWP and CSM) */
+ internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime);
+
+ if (internal->status == DVBS2_DEMOD_LOCK) {
+ dprintk(state->verbose, FE_DEBUG, 1, "------------> DVB-S2 DEMOD LOCK !");
+ i = 0;
+ /* Demod Locked, check FEC status */
+ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime);
+
+ /*If false lock (UWP and CSM Locked but no FEC) try 3 time max*/
+ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) {
+ /* Read the frequency offset*/
+ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ);
+
+ /* Set the Nominal frequency to the found frequency offset for the next reacquire*/
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ);
+ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg);
+ stb0899_dvbs2_reacquire(state);
+ internal->status = stb0899_dvbs2_get_fec_status(state, searchTime);
+ i++;
+ }
+ }
+
+ if (internal->status != DVBS2_FEC_LOCK) {
+ if (internal->inversion == IQ_SWAP_AUTO) {
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2);
+ iqSpectrum = STB0899_GETFIELD(SPECTRUM_INVERT, reg);
+ /* IQ Spectrum Inversion */
+ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, !iqSpectrum);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg);
+ /* start acquistion process */
+ stb0899_dvbs2_reacquire(state);
+
+ /* Wait for demod lock (UWP and CSM) */
+ internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime);
+ if (internal->status == DVBS2_DEMOD_LOCK) {
+ i = 0;
+ /* Demod Locked, check FEC */
+ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime);
+ /*try thrice for false locks, (UWP and CSM Locked but no FEC) */
+ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) {
+ /* Read the frequency offset*/
+ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ);
+
+ /* Set the Nominal frequency to the found frequency offset for the next reacquire*/
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ);
+ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg);
+
+ stb0899_dvbs2_reacquire(state);
+ internal->status = stb0899_dvbs2_get_fec_status(state, searchTime);
+ i++;
+ }
+ }
+/*
+ if (pParams->DVBS2State == FE_DVBS2_FEC_LOCKED)
+ pParams->IQLocked = !iqSpectrum;
+*/
+ }
+ }
+ if (internal->status == DVBS2_FEC_LOCK) {
+ dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 FEC Lock !");
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2);
+ modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2;
+ pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01;
+
+ if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) &&
+ (INRANGE(STB0899_QPSK_23, modcod, STB0899_QPSK_910)) &&
+ (pilots == 1)) {
+
+ stb0899_dvbs2_init_csm(state, pilots, modcod);
+ /* Wait for UWP,CSM and data LOCK 20ms max */
+ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime);
+
+ i = 0;
+ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) {
+ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1);
+ STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1);
+ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1);
+ STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 0);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1);
+
+ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime);
+ i++;
+ }
+ }
+
+ if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) &&
+ (INRANGE(STB0899_QPSK_12, modcod, STB0899_QPSK_35)) &&
+ (pilots == 1)) {
+
+ /* Equalizer Disable update */
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL);
+ STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg);
+ }
+
+ /* slow down the Equalizer once locked */
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL);
+ STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0x02);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg);
+
+ /* Store signal parameters */
+ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ);
+
+ offsetfreq = offsetfreq / ((1 << 30) / 1000);
+ offsetfreq *= (internal->master_clk / 1000000);
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2);
+ if (STB0899_GETFIELD(SPECTRUM_INVERT, reg))
+ offsetfreq *= -1;
+
+ internal->freq = internal->freq - offsetfreq;
+ internal->srate = stb0899_dvbs2_get_srate(state);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2);
+ internal->modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2;
+ internal->pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01;
+ internal->frame_length = (STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 1) & 0x01;
+
+ /* Set IF AGC to tracking */
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL);
+ STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 3);
+
+ /* if QPSK 1/2,QPSK 3/5 or QPSK 2/3 set IF AGC reference to 16 otherwise 32*/
+ if (INRANGE(STB0899_QPSK_12, internal->modcod, STB0899_QPSK_23))
+ STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 16);
+
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2);
+ STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 7);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg);
+ }
+
+ /* Release Stream Merger Reset */
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESRS, reg, 0);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ return internal->status;
+}
diff --git a/drivers/media/dvb/frontends/stb0899_cfg.h b/drivers/media/dvb/frontends/stb0899_cfg.h
new file mode 100644
index 000000000000..0867906d3ff3
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb0899_cfg.h
@@ -0,0 +1,287 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ 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 __STB0899_CFG_H
+#define __STB0899_CFG_H
+
+static const struct stb0899_s2_reg stb0899_s2_init_2[] = {
+
+ { STB0899_OFF0_DMD_STATUS , STB0899_BASE_DMD_STATUS , 0x00000103 }, /* DMDSTATUS */
+ { STB0899_OFF0_CRL_FREQ , STB0899_BASE_CRL_FREQ , 0x3ed1da56 }, /* CRLFREQ */
+ { STB0899_OFF0_BTR_FREQ , STB0899_BASE_BTR_FREQ , 0x00004000 }, /* BTRFREQ */
+ { STB0899_OFF0_IF_AGC_GAIN , STB0899_BASE_IF_AGC_GAIN , 0x00002ade }, /* IFAGCGAIN */
+ { STB0899_OFF0_BB_AGC_GAIN , STB0899_BASE_BB_AGC_GAIN , 0x000001bc }, /* BBAGCGAIN */
+ { STB0899_OFF0_DC_OFFSET , STB0899_BASE_DC_OFFSET , 0x00000200 }, /* DCOFFSET */
+ { STB0899_OFF0_DMD_CNTRL , STB0899_BASE_DMD_CNTRL , 0x0000000f }, /* DMDCNTRL */
+
+ { STB0899_OFF0_IF_AGC_CNTRL , STB0899_BASE_IF_AGC_CNTRL , 0x03fb4a20 }, /* IFAGCCNTRL */
+ { STB0899_OFF0_BB_AGC_CNTRL , STB0899_BASE_BB_AGC_CNTRL , 0x00200c97 }, /* BBAGCCNTRL */
+
+ { STB0899_OFF0_CRL_CNTRL , STB0899_BASE_CRL_CNTRL , 0x00000016 }, /* CRLCNTRL */
+ { STB0899_OFF0_CRL_PHS_INIT , STB0899_BASE_CRL_PHS_INIT , 0x00000000 }, /* CRLPHSINIT */
+ { STB0899_OFF0_CRL_FREQ_INIT , STB0899_BASE_CRL_FREQ_INIT , 0x00000000 }, /* CRLFREQINIT */
+ { STB0899_OFF0_CRL_LOOP_GAIN , STB0899_BASE_CRL_LOOP_GAIN , 0x00000000 }, /* CRLLOOPGAIN */
+ { STB0899_OFF0_CRL_NOM_FREQ , STB0899_BASE_CRL_NOM_FREQ , 0x3ed097b6 }, /* CRLNOMFREQ */
+ { STB0899_OFF0_CRL_SWP_RATE , STB0899_BASE_CRL_SWP_RATE , 0x00000000 }, /* CRLSWPRATE */
+ { STB0899_OFF0_CRL_MAX_SWP , STB0899_BASE_CRL_MAX_SWP , 0x00000000 }, /* CRLMAXSWP */
+ { STB0899_OFF0_CRL_LK_CNTRL , STB0899_BASE_CRL_LK_CNTRL , 0x0f6cdc01 }, /* CRLLKCNTRL */
+ { STB0899_OFF0_DECIM_CNTRL , STB0899_BASE_DECIM_CNTRL , 0x00000000 }, /* DECIMCNTRL */
+ { STB0899_OFF0_BTR_CNTRL , STB0899_BASE_BTR_CNTRL , 0x00003993 }, /* BTRCNTRL */
+ { STB0899_OFF0_BTR_LOOP_GAIN , STB0899_BASE_BTR_LOOP_GAIN , 0x000d3c6f }, /* BTRLOOPGAIN */
+ { STB0899_OFF0_BTR_PHS_INIT , STB0899_BASE_BTR_PHS_INIT , 0x00000000 }, /* BTRPHSINIT */
+ { STB0899_OFF0_BTR_FREQ_INIT , STB0899_BASE_BTR_FREQ_INIT , 0x00000000 }, /* BTRFREQINIT */
+ { STB0899_OFF0_BTR_NOM_FREQ , STB0899_BASE_BTR_NOM_FREQ , 0x0238e38e }, /* BTRNOMFREQ */
+ { STB0899_OFF0_BTR_LK_CNTRL , STB0899_BASE_BTR_LK_CNTRL , 0x00000000 }, /* BTRLKCNTRL */
+ { STB0899_OFF0_DECN_CNTRL , STB0899_BASE_DECN_CNTRL , 0x00000000 }, /* DECNCNTRL */
+ { STB0899_OFF0_TP_CNTRL , STB0899_BASE_TP_CNTRL , 0x00000000 }, /* TPCNTRL */
+ { STB0899_OFF0_TP_BUF_STATUS , STB0899_BASE_TP_BUF_STATUS , 0x00000000 }, /* TPBUFSTATUS */
+ { STB0899_OFF0_DC_ESTIM , STB0899_BASE_DC_ESTIM , 0x00000000 }, /* DCESTIM */
+ { STB0899_OFF0_FLL_CNTRL , STB0899_BASE_FLL_CNTRL , 0x00000000 }, /* FLLCNTRL */
+ { STB0899_OFF0_FLL_FREQ_WD , STB0899_BASE_FLL_FREQ_WD , 0x40070000 }, /* FLLFREQWD */
+ { STB0899_OFF0_ANTI_ALIAS_SEL , STB0899_BASE_ANTI_ALIAS_SEL , 0x00000001 }, /* ANTIALIASSEL */
+ { STB0899_OFF0_RRC_ALPHA , STB0899_BASE_RRC_ALPHA , 0x00000002 }, /* RRCALPHA */
+ { STB0899_OFF0_DC_ADAPT_LSHFT , STB0899_BASE_DC_ADAPT_LSHFT , 0x00000000 }, /* DCADAPTISHFT */
+ { STB0899_OFF0_IMB_OFFSET , STB0899_BASE_IMB_OFFSET , 0x0000fe01 }, /* IMBOFFSET */
+ { STB0899_OFF0_IMB_ESTIMATE , STB0899_BASE_IMB_ESTIMATE , 0x00000000 }, /* IMBESTIMATE */
+ { STB0899_OFF0_IMB_CNTRL , STB0899_BASE_IMB_CNTRL , 0x00000001 }, /* IMBCNTRL */
+ { STB0899_OFF0_IF_AGC_CNTRL2 , STB0899_BASE_IF_AGC_CNTRL2 , 0x00005007 }, /* IFAGCCNTRL2 */
+ { STB0899_OFF0_DMD_CNTRL2 , STB0899_BASE_DMD_CNTRL2 , 0x00000002 }, /* DMDCNTRL2 */
+ { STB0899_OFF0_TP_BUFFER , STB0899_BASE_TP_BUFFER , 0x00000000 }, /* TPBUFFER */
+ { STB0899_OFF0_TP_BUFFER1 , STB0899_BASE_TP_BUFFER1 , 0x00000000 }, /* TPBUFFER1 */
+ { STB0899_OFF0_TP_BUFFER2 , STB0899_BASE_TP_BUFFER2 , 0x00000000 }, /* TPBUFFER2 */
+ { STB0899_OFF0_TP_BUFFER3 , STB0899_BASE_TP_BUFFER3 , 0x00000000 }, /* TPBUFFER3 */
+ { STB0899_OFF0_TP_BUFFER4 , STB0899_BASE_TP_BUFFER4 , 0x00000000 }, /* TPBUFFER4 */
+ { STB0899_OFF0_TP_BUFFER5 , STB0899_BASE_TP_BUFFER5 , 0x00000000 }, /* TPBUFFER5 */
+ { STB0899_OFF0_TP_BUFFER6 , STB0899_BASE_TP_BUFFER6 , 0x00000000 }, /* TPBUFFER6 */
+ { STB0899_OFF0_TP_BUFFER7 , STB0899_BASE_TP_BUFFER7 , 0x00000000 }, /* TPBUFFER7 */
+ { STB0899_OFF0_TP_BUFFER8 , STB0899_BASE_TP_BUFFER8 , 0x00000000 }, /* TPBUFFER8 */
+ { STB0899_OFF0_TP_BUFFER9 , STB0899_BASE_TP_BUFFER9 , 0x00000000 }, /* TPBUFFER9 */
+ { STB0899_OFF0_TP_BUFFER10 , STB0899_BASE_TP_BUFFER10 , 0x00000000 }, /* TPBUFFER10 */
+ { STB0899_OFF0_TP_BUFFER11 , STB0899_BASE_TP_BUFFER11 , 0x00000000 }, /* TPBUFFER11 */
+ { STB0899_OFF0_TP_BUFFER12 , STB0899_BASE_TP_BUFFER12 , 0x00000000 }, /* TPBUFFER12 */
+ { STB0899_OFF0_TP_BUFFER13 , STB0899_BASE_TP_BUFFER13 , 0x00000000 }, /* TPBUFFER13 */
+ { STB0899_OFF0_TP_BUFFER14 , STB0899_BASE_TP_BUFFER14 , 0x00000000 }, /* TPBUFFER14 */
+ { STB0899_OFF0_TP_BUFFER15 , STB0899_BASE_TP_BUFFER15 , 0x00000000 }, /* TPBUFFER15 */
+ { STB0899_OFF0_TP_BUFFER16 , STB0899_BASE_TP_BUFFER16 , 0x0000ff00 }, /* TPBUFFER16 */
+ { STB0899_OFF0_TP_BUFFER17 , STB0899_BASE_TP_BUFFER17 , 0x00000100 }, /* TPBUFFER17 */
+ { STB0899_OFF0_TP_BUFFER18 , STB0899_BASE_TP_BUFFER18 , 0x0000fe01 }, /* TPBUFFER18 */
+ { STB0899_OFF0_TP_BUFFER19 , STB0899_BASE_TP_BUFFER19 , 0x000004fe }, /* TPBUFFER19 */
+ { STB0899_OFF0_TP_BUFFER20 , STB0899_BASE_TP_BUFFER20 , 0x0000cfe7 }, /* TPBUFFER20 */
+ { STB0899_OFF0_TP_BUFFER21 , STB0899_BASE_TP_BUFFER21 , 0x0000bec6 }, /* TPBUFFER21 */
+ { STB0899_OFF0_TP_BUFFER22 , STB0899_BASE_TP_BUFFER22 , 0x0000c2bf }, /* TPBUFFER22 */
+ { STB0899_OFF0_TP_BUFFER23 , STB0899_BASE_TP_BUFFER23 , 0x0000c1c1 }, /* TPBUFFER23 */
+ { STB0899_OFF0_TP_BUFFER24 , STB0899_BASE_TP_BUFFER24 , 0x0000c1c1 }, /* TPBUFFER24 */
+ { STB0899_OFF0_TP_BUFFER25 , STB0899_BASE_TP_BUFFER25 , 0x0000c1c1 }, /* TPBUFFER25 */
+ { STB0899_OFF0_TP_BUFFER26 , STB0899_BASE_TP_BUFFER26 , 0x0000c1c1 }, /* TPBUFFER26 */
+ { STB0899_OFF0_TP_BUFFER27 , STB0899_BASE_TP_BUFFER27 , 0x0000c1c0 }, /* TPBUFFER27 */
+ { STB0899_OFF0_TP_BUFFER28 , STB0899_BASE_TP_BUFFER28 , 0x0000c0c0 }, /* TPBUFFER28 */
+ { STB0899_OFF0_TP_BUFFER29 , STB0899_BASE_TP_BUFFER29 , 0x0000c1c1 }, /* TPBUFFER29 */
+ { STB0899_OFF0_TP_BUFFER30 , STB0899_BASE_TP_BUFFER30 , 0x0000c1c1 }, /* TPBUFFER30 */
+ { STB0899_OFF0_TP_BUFFER31 , STB0899_BASE_TP_BUFFER31 , 0x0000c0c1 }, /* TPBUFFER31 */
+ { STB0899_OFF0_TP_BUFFER32 , STB0899_BASE_TP_BUFFER32 , 0x0000c0c1 }, /* TPBUFFER32 */
+ { STB0899_OFF0_TP_BUFFER33 , STB0899_BASE_TP_BUFFER33 , 0x0000c1c1 }, /* TPBUFFER33 */
+ { STB0899_OFF0_TP_BUFFER34 , STB0899_BASE_TP_BUFFER34 , 0x0000c1c1 }, /* TPBUFFER34 */
+ { STB0899_OFF0_TP_BUFFER35 , STB0899_BASE_TP_BUFFER35 , 0x0000c0c1 }, /* TPBUFFER35 */
+ { STB0899_OFF0_TP_BUFFER36 , STB0899_BASE_TP_BUFFER36 , 0x0000c1c1 }, /* TPBUFFER36 */
+ { STB0899_OFF0_TP_BUFFER37 , STB0899_BASE_TP_BUFFER37 , 0x0000c0c1 }, /* TPBUFFER37 */
+ { STB0899_OFF0_TP_BUFFER38 , STB0899_BASE_TP_BUFFER38 , 0x0000c1c1 }, /* TPBUFFER38 */
+ { STB0899_OFF0_TP_BUFFER39 , STB0899_BASE_TP_BUFFER39 , 0x0000c0c0 }, /* TPBUFFER39 */
+ { STB0899_OFF0_TP_BUFFER40 , STB0899_BASE_TP_BUFFER40 , 0x0000c1c0 }, /* TPBUFFER40 */
+ { STB0899_OFF0_TP_BUFFER41 , STB0899_BASE_TP_BUFFER41 , 0x0000c1c1 }, /* TPBUFFER41 */
+ { STB0899_OFF0_TP_BUFFER42 , STB0899_BASE_TP_BUFFER42 , 0x0000c0c0 }, /* TPBUFFER42 */
+ { STB0899_OFF0_TP_BUFFER43 , STB0899_BASE_TP_BUFFER43 , 0x0000c1c0 }, /* TPBUFFER43 */
+ { STB0899_OFF0_TP_BUFFER44 , STB0899_BASE_TP_BUFFER44 , 0x0000c0c1 }, /* TPBUFFER44 */
+ { STB0899_OFF0_TP_BUFFER45 , STB0899_BASE_TP_BUFFER45 , 0x0000c1be }, /* TPBUFFER45 */
+ { STB0899_OFF0_TP_BUFFER46 , STB0899_BASE_TP_BUFFER46 , 0x0000c1c9 }, /* TPBUFFER46 */
+ { STB0899_OFF0_TP_BUFFER47 , STB0899_BASE_TP_BUFFER47 , 0x0000c0da }, /* TPBUFFER47 */
+ { STB0899_OFF0_TP_BUFFER48 , STB0899_BASE_TP_BUFFER48 , 0x0000c0ba }, /* TPBUFFER48 */
+ { STB0899_OFF0_TP_BUFFER49 , STB0899_BASE_TP_BUFFER49 , 0x0000c1c4 }, /* TPBUFFER49 */
+ { STB0899_OFF0_TP_BUFFER50 , STB0899_BASE_TP_BUFFER50 , 0x0000c1bf }, /* TPBUFFER50 */
+ { STB0899_OFF0_TP_BUFFER51 , STB0899_BASE_TP_BUFFER51 , 0x0000c0c1 }, /* TPBUFFER51 */
+ { STB0899_OFF0_TP_BUFFER52 , STB0899_BASE_TP_BUFFER52 , 0x0000c1c0 }, /* TPBUFFER52 */
+ { STB0899_OFF0_TP_BUFFER53 , STB0899_BASE_TP_BUFFER53 , 0x0000c0c1 }, /* TPBUFFER53 */
+ { STB0899_OFF0_TP_BUFFER54 , STB0899_BASE_TP_BUFFER54 , 0x0000c1c1 }, /* TPBUFFER54 */
+ { STB0899_OFF0_TP_BUFFER55 , STB0899_BASE_TP_BUFFER55 , 0x0000c1c1 }, /* TPBUFFER55 */
+ { STB0899_OFF0_TP_BUFFER56 , STB0899_BASE_TP_BUFFER56 , 0x0000c1c1 }, /* TPBUFFER56 */
+ { STB0899_OFF0_TP_BUFFER57 , STB0899_BASE_TP_BUFFER57 , 0x0000c1c1 }, /* TPBUFFER57 */
+ { STB0899_OFF0_TP_BUFFER58 , STB0899_BASE_TP_BUFFER58 , 0x0000c1c1 }, /* TPBUFFER58 */
+ { STB0899_OFF0_TP_BUFFER59 , STB0899_BASE_TP_BUFFER59 , 0x0000c1c1 }, /* TPBUFFER59 */
+ { STB0899_OFF0_TP_BUFFER60 , STB0899_BASE_TP_BUFFER60 , 0x0000c1c1 }, /* TPBUFFER60 */
+ { STB0899_OFF0_TP_BUFFER61 , STB0899_BASE_TP_BUFFER61 , 0x0000c1c1 }, /* TPBUFFER61 */
+ { STB0899_OFF0_TP_BUFFER62 , STB0899_BASE_TP_BUFFER62 , 0x0000c1c1 }, /* TPBUFFER62 */
+ { STB0899_OFF0_TP_BUFFER63 , STB0899_BASE_TP_BUFFER63 , 0x0000c1c0 }, /* TPBUFFER63 */
+ { STB0899_OFF0_RESET_CNTRL , STB0899_BASE_RESET_CNTRL , 0x00000001 }, /* RESETCNTRL */
+ { STB0899_OFF0_ACM_ENABLE , STB0899_BASE_ACM_ENABLE , 0x00005654 }, /* ACMENABLE */
+ { STB0899_OFF0_DESCR_CNTRL , STB0899_BASE_DESCR_CNTRL , 0x00000000 }, /* DESCRCNTRL */
+ { STB0899_OFF0_CSM_CNTRL1 , STB0899_BASE_CSM_CNTRL1 , 0x00020019 }, /* CSMCNTRL1 */
+ { STB0899_OFF0_CSM_CNTRL2 , STB0899_BASE_CSM_CNTRL2 , 0x004b3237 }, /* CSMCNTRL2 */
+ { STB0899_OFF0_CSM_CNTRL3 , STB0899_BASE_CSM_CNTRL3 , 0x0003dd17 }, /* CSMCNTRL3 */
+ { STB0899_OFF0_CSM_CNTRL4 , STB0899_BASE_CSM_CNTRL4 , 0x00008008 }, /* CSMCNTRL4 */
+ { STB0899_OFF0_UWP_CNTRL1 , STB0899_BASE_UWP_CNTRL1 , 0x002a3106 }, /* UWPCNTRL1 */
+ { STB0899_OFF0_UWP_CNTRL2 , STB0899_BASE_UWP_CNTRL2 , 0x0006140a }, /* UWPCNTRL2 */
+ { STB0899_OFF0_UWP_STAT1 , STB0899_BASE_UWP_STAT1 , 0x00008000 }, /* UWPSTAT1 */
+ { STB0899_OFF0_UWP_STAT2 , STB0899_BASE_UWP_STAT2 , 0x00000000 }, /* UWPSTAT2 */
+ { STB0899_OFF0_DMD_STAT2 , STB0899_BASE_DMD_STAT2 , 0x00000000 }, /* DMDSTAT2 */
+ { STB0899_OFF0_FREQ_ADJ_SCALE , STB0899_BASE_FREQ_ADJ_SCALE , 0x00000471 }, /* FREQADJSCALE */
+ { STB0899_OFF0_UWP_CNTRL3 , STB0899_BASE_UWP_CNTRL3 , 0x017b0465 }, /* UWPCNTRL3 */
+ { STB0899_OFF0_SYM_CLK_SEL , STB0899_BASE_SYM_CLK_SEL , 0x00000002 }, /* SYMCLKSEL */
+ { STB0899_OFF0_SOF_SRCH_TO , STB0899_BASE_SOF_SRCH_TO , 0x00196464 }, /* SOFSRCHTO */
+ { STB0899_OFF0_ACQ_CNTRL1 , STB0899_BASE_ACQ_CNTRL1 , 0x00000603 }, /* ACQCNTRL1 */
+ { STB0899_OFF0_ACQ_CNTRL2 , STB0899_BASE_ACQ_CNTRL2 , 0x02046666 }, /* ACQCNTRL2 */
+ { STB0899_OFF0_ACQ_CNTRL3 , STB0899_BASE_ACQ_CNTRL3 , 0x10046583 }, /* ACQCNTRL3 */
+ { STB0899_OFF0_FE_SETTLE , STB0899_BASE_FE_SETTLE , 0x00010404 }, /* FESETTLE */
+ { STB0899_OFF0_AC_DWELL , STB0899_BASE_AC_DWELL , 0x0002aa8a }, /* ACDWELL */
+ { STB0899_OFF0_ACQUIRE_TRIG , STB0899_BASE_ACQUIRE_TRIG , 0x00000000 }, /* ACQUIRETRIG */
+ { STB0899_OFF0_LOCK_LOST , STB0899_BASE_LOCK_LOST , 0x00000001 }, /* LOCKLOST */
+ { STB0899_OFF0_ACQ_STAT1 , STB0899_BASE_ACQ_STAT1 , 0x00000500 }, /* ACQSTAT1 */
+ { STB0899_OFF0_ACQ_TIMEOUT , STB0899_BASE_ACQ_TIMEOUT , 0x0028a0a0 }, /* ACQTIMEOUT */
+ { STB0899_OFF0_ACQ_TIME , STB0899_BASE_ACQ_TIME , 0x00000000 }, /* ACQTIME */
+ { STB0899_OFF0_FINAL_AGC_CNTRL , STB0899_BASE_FINAL_AGC_CNTRL , 0x00800c17 }, /* FINALAGCCNTRL*/
+ { STB0899_OFF0_FINAL_AGC_GAIN , STB0899_BASE_FINAL_AGC_GAIN , 0x00000000 }, /* FINALAGCCGAIN*/
+ { STB0899_OFF0_EQUALIZER_INIT , STB0899_BASE_EQUALIZER_INIT , 0x00000000 }, /* EQUILIZERINIT*/
+ { STB0899_OFF0_EQ_CNTRL , STB0899_BASE_EQ_CNTRL , 0x00054802 }, /* EQCNTL */
+ { STB0899_OFF0_EQ_I_INIT_COEFF_0, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF0 */
+ { STB0899_OFF1_EQ_I_INIT_COEFF_1, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF1 */
+ { STB0899_OFF2_EQ_I_INIT_COEFF_2, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF2 */
+ { STB0899_OFF3_EQ_I_INIT_COEFF_3, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF3 */
+ { STB0899_OFF4_EQ_I_INIT_COEFF_4, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF4 */
+ { STB0899_OFF5_EQ_I_INIT_COEFF_5, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000400 }, /* EQIINITCOEFF5 */
+ { STB0899_OFF6_EQ_I_INIT_COEFF_6, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF6 */
+ { STB0899_OFF7_EQ_I_INIT_COEFF_7, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF7 */
+ { STB0899_OFF8_EQ_I_INIT_COEFF_8, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF8 */
+ { STB0899_OFF9_EQ_I_INIT_COEFF_9, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF9 */
+ { STB0899_OFFa_EQ_I_INIT_COEFF_10,STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF10*/
+ { STB0899_OFF0_EQ_Q_INIT_COEFF_0, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF0 */
+ { STB0899_OFF1_EQ_Q_INIT_COEFF_1, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF1 */
+ { STB0899_OFF2_EQ_Q_INIT_COEFF_2, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF2 */
+ { STB0899_OFF3_EQ_Q_INIT_COEFF_3, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF3 */
+ { STB0899_OFF4_EQ_Q_INIT_COEFF_4, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF4 */
+ { STB0899_OFF5_EQ_Q_INIT_COEFF_5, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF5 */
+ { STB0899_OFF6_EQ_Q_INIT_COEFF_6, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF6 */
+ { STB0899_OFF7_EQ_Q_INIT_COEFF_7, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF7 */
+ { STB0899_OFF8_EQ_Q_INIT_COEFF_8, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF8 */
+ { STB0899_OFF9_EQ_Q_INIT_COEFF_9, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF9 */
+ { STB0899_OFFa_EQ_Q_INIT_COEFF_10,STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF10*/
+ { STB0899_OFF0_EQ_I_OUT_COEFF_0 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT0 */
+ { STB0899_OFF1_EQ_I_OUT_COEFF_1 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT1 */
+ { STB0899_OFF2_EQ_I_OUT_COEFF_2 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT2 */
+ { STB0899_OFF3_EQ_I_OUT_COEFF_3 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT3 */
+ { STB0899_OFF4_EQ_I_OUT_COEFF_4 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT4 */
+ { STB0899_OFF5_EQ_I_OUT_COEFF_5 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT5 */
+ { STB0899_OFF6_EQ_I_OUT_COEFF_6 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT6 */
+ { STB0899_OFF7_EQ_I_OUT_COEFF_7 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT7 */
+ { STB0899_OFF8_EQ_I_OUT_COEFF_8 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT8 */
+ { STB0899_OFF9_EQ_I_OUT_COEFF_9 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT9 */
+ { STB0899_OFFa_EQ_I_OUT_COEFF_10,STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT10*/
+ { STB0899_OFF0_EQ_Q_OUT_COEFF_0 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT0 */
+ { STB0899_OFF1_EQ_Q_OUT_COEFF_1 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT1 */
+ { STB0899_OFF2_EQ_Q_OUT_COEFF_2 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT2 */
+ { STB0899_OFF3_EQ_Q_OUT_COEFF_3 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT3 */
+ { STB0899_OFF4_EQ_Q_OUT_COEFF_4 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT4 */
+ { STB0899_OFF5_EQ_Q_OUT_COEFF_5 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT5 */
+ { STB0899_OFF6_EQ_Q_OUT_COEFF_6 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT6 */
+ { STB0899_OFF7_EQ_Q_OUT_COEFF_7 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT7 */
+ { STB0899_OFF8_EQ_Q_OUT_COEFF_8 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT8 */
+ { STB0899_OFF9_EQ_Q_OUT_COEFF_9 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT9 */
+ { STB0899_OFFa_EQ_Q_OUT_COEFF_10, STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT10*/
+ { 0xffff , 0xffffffff , 0xffffffff },
+};
+static const struct stb0899_s2_reg stb0899_s2_init_4[] = {
+ { STB0899_OFF0_BLOCK_LNGTH , STB0899_BASE_BLOCK_LNGTH , 0x00000008 }, /* BLOCKLNGTH */
+ { STB0899_OFF0_ROW_STR , STB0899_BASE_ROW_STR , 0x000000b4 }, /* ROWSTR */
+ { STB0899_OFF0_BN_END_ADDR , STB0899_BASE_BN_END_ADDR , 0x000004b5 }, /* BNANDADDR */
+ { STB0899_OFF0_CN_END_ADDR , STB0899_BASE_CN_END_ADDR , 0x00000b4b }, /* CNANDADDR */
+ { STB0899_OFF0_INFO_LENGTH , STB0899_BASE_INFO_LENGTH , 0x00000078 }, /* INFOLENGTH */
+ { STB0899_OFF0_BOT_ADDR , STB0899_BASE_BOT_ADDR , 0x000001e0 }, /* BOT_ADDR */
+ { STB0899_OFF0_BCH_BLK_LN , STB0899_BASE_BCH_BLK_LN , 0x0000a8c0 }, /* BCHBLKLN */
+ { STB0899_OFF0_BCH_T , STB0899_BASE_BCH_T , 0x0000000c }, /* BCHT */
+ { STB0899_OFF0_CNFG_MODE , STB0899_BASE_CNFG_MODE , 0x00000001 }, /* CNFGMODE */
+ { STB0899_OFF0_LDPC_STAT , STB0899_BASE_LDPC_STAT , 0x0000000d }, /* LDPCSTAT */
+ { STB0899_OFF0_ITER_SCALE , STB0899_BASE_ITER_SCALE , 0x00000040 }, /* ITERSCALE */
+ { STB0899_OFF0_INPUT_MODE , STB0899_BASE_INPUT_MODE , 0x00000000 }, /* INPUTMODE */
+ { STB0899_OFF0_LDPCDECRST , STB0899_BASE_LDPCDECRST , 0x00000000 }, /* LDPCDECRST */
+ { STB0899_OFF0_CLK_PER_BYTE_RW , STB0899_BASE_CLK_PER_BYTE_RW , 0x00000008 }, /* CLKPERBYTE */
+ { STB0899_OFF0_BCH_ERRORS , STB0899_BASE_BCH_ERRORS , 0x00000000 }, /* BCHERRORS */
+ { STB0899_OFF0_LDPC_ERRORS , STB0899_BASE_LDPC_ERRORS , 0x00000000 }, /* LDPCERRORS */
+ { STB0899_OFF0_BCH_MODE , STB0899_BASE_BCH_MODE , 0x00000000 }, /* BCHMODE */
+ { STB0899_OFF0_ERR_ACC_PER , STB0899_BASE_ERR_ACC_PER , 0x00000008 }, /* ERRACCPER */
+ { STB0899_OFF0_BCH_ERR_ACC , STB0899_BASE_BCH_ERR_ACC , 0x00000000 }, /* BCHERRACC */
+ { STB0899_OFF0_FEC_TP_SEL , STB0899_BASE_FEC_TP_SEL , 0x00000000 }, /* FECTPSEL */
+ { 0xffff , 0xffffffff , 0xffffffff },
+};
+
+static const struct stb0899_s1_reg stb0899_s1_init_5[] = {
+ { STB0899_TSTCK , 0x00 },
+ { STB0899_TSTRES , 0x00 },
+ { STB0899_TSTOUT , 0x00 },
+ { STB0899_TSTIN , 0x00 },
+ { STB0899_TSTSYS , 0x00 },
+ { STB0899_TSTCHIP , 0x00 },
+ { STB0899_TSTFREE , 0x00 },
+ { STB0899_TSTI2C , 0x00 },
+ { STB0899_BITSPEEDM , 0x00 },
+ { STB0899_BITSPEEDL , 0x00 },
+ { STB0899_TBUSBIT , 0x00 },
+ { STB0899_TSTDIS , 0x00 },
+ { STB0899_TSTDISRX , 0x00 },
+ { STB0899_TSTJETON , 0x00 },
+ { STB0899_TSTDCADJ , 0x00 },
+ { STB0899_TSTAGC1 , 0x00 },
+ { STB0899_TSTAGC1N , 0x00 },
+ { STB0899_TSTPOLYPH , 0x00 },
+ { STB0899_TSTR , 0x00 },
+ { STB0899_TSTAGC2 , 0x00 },
+ { STB0899_TSTCTL1 , 0x00 },
+ { STB0899_TSTCTL2 , 0x00 },
+ { STB0899_TSTCTL3 , 0x00 },
+ { STB0899_TSTDEMAP , 0x00 },
+ { STB0899_TSTDEMAP2 , 0x00 },
+ { STB0899_TSTDEMMON , 0x00 },
+ { STB0899_TSTRATE , 0x00 },
+ { STB0899_TSTSELOUT , 0x00 },
+ { STB0899_TSYNC , 0x00 },
+ { STB0899_TSTERR , 0x00 },
+ { STB0899_TSTRAM1 , 0x00 },
+ { STB0899_TSTVSELOUT , 0x00 },
+ { STB0899_TSTFORCEIN , 0x00 },
+ { STB0899_TSTRS1 , 0x00 },
+ { STB0899_TSTRS2 , 0x00 },
+ { STB0899_TSTRS3 , 0x00 },
+ { STB0899_GHOSTREG , 0x81 },
+ { 0xffff , 0xff },
+};
+
+#define STB0899_DVBS2_ESNO_AVE 3
+#define STB0899_DVBS2_ESNO_QUANT 32
+#define STB0899_DVBS2_AVFRAMES_COARSE 10
+#define STB0899_DVBS2_AVFRAMES_FINE 20
+#define STB0899_DVBS2_MISS_THRESHOLD 6
+#define STB0899_DVBS2_UWP_THRESHOLD_ACQ 1125
+#define STB0899_DVBS2_UWP_THRESHOLD_TRACK 758
+#define STB0899_DVBS2_UWP_THRESHOLD_SOF 1350
+#define STB0899_DVBS2_SOF_SEARCH_TIMEOUT 1664100
+
+#define STB0899_DVBS2_BTR_NCO_BITS 28
+#define STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET 15
+#define STB0899_DVBS2_CRL_NCO_BITS 30
+#define STB0899_DVBS2_LDPC_MAX_ITER 70
+
+#endif //__STB0899_CFG_H
diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c
new file mode 100644
index 000000000000..528820170228
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb0899_drv.c
@@ -0,0 +1,1684 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+#include "stb0899_drv.h"
+#include "stb0899_priv.h"
+#include "stb0899_reg.h"
+
+static unsigned int verbose = 0;//1;
+module_param(verbose, int, 0644);
+
+/* C/N in dB/10, NIRM/NIRL */
+static const struct stb0899_tab stb0899_cn_tab[] = {
+ { 200, 2600 },
+ { 190, 2700 },
+ { 180, 2860 },
+ { 170, 3020 },
+ { 160, 3210 },
+ { 150, 3440 },
+ { 140, 3710 },
+ { 130, 4010 },
+ { 120, 4360 },
+ { 110, 4740 },
+ { 100, 5190 },
+ { 90, 5670 },
+ { 80, 6200 },
+ { 70, 6770 },
+ { 60, 7360 },
+ { 50, 7970 },
+ { 40, 8250 },
+ { 30, 9000 },
+ { 20, 9450 },
+ { 15, 9600 },
+};
+
+/* DVB-S AGCIQ_VALUE vs. signal level in dBm/10.
+ * As measured, connected to a modulator.
+ * -8.0 to -50.0 dBm directly connected,
+ * -52.0 to -74.8 with extra attenuation.
+ * Cut-off to AGCIQ_VALUE = 0x80 below -74.8dBm.
+ * Crude linear extrapolation below -84.8dBm and above -8.0dBm.
+ */
+static const struct stb0899_tab stb0899_dvbsrf_tab[] = {
+ { -950, -128 },
+ { -748, -94 },
+ { -745, -92 },
+ { -735, -90 },
+ { -720, -87 },
+ { -670, -77 },
+ { -640, -70 },
+ { -610, -62 },
+ { -600, -60 },
+ { -590, -56 },
+ { -560, -41 },
+ { -540, -25 },
+ { -530, -17 },
+ { -520, -11 },
+ { -500, 1 },
+ { -490, 6 },
+ { -480, 10 },
+ { -440, 22 },
+ { -420, 27 },
+ { -400, 31 },
+ { -380, 34 },
+ { -340, 40 },
+ { -320, 43 },
+ { -280, 48 },
+ { -250, 52 },
+ { -230, 55 },
+ { -180, 61 },
+ { -140, 66 },
+ { -90, 73 },
+ { -80, 74 },
+ { 500, 127 }
+};
+
+/* DVB-S2 IF_AGC_GAIN vs. signal level in dBm/10.
+ * As measured, connected to a modulator.
+ * -8.0 to -50.1 dBm directly connected,
+ * -53.0 to -76.6 with extra attenuation.
+ * Cut-off to IF_AGC_GAIN = 0x3fff below -76.6dBm.
+ * Crude linear extrapolation below -76.6dBm and above -8.0dBm.
+ */
+static const struct stb0899_tab stb0899_dvbs2rf_tab[] = {
+ { 700, 0 },
+ { -80, 3217 },
+ { -150, 3893 },
+ { -190, 4217 },
+ { -240, 4621 },
+ { -280, 4945 },
+ { -320, 5273 },
+ { -350, 5545 },
+ { -370, 5741 },
+ { -410, 6147 },
+ { -450, 6671 },
+ { -490, 7413 },
+ { -501, 7665 },
+ { -530, 8767 },
+ { -560, 10219 },
+ { -580, 10939 },
+ { -590, 11518 },
+ { -600, 11723 },
+ { -650, 12659 },
+ { -690, 13219 },
+ { -730, 13645 },
+ { -750, 13909 },
+ { -766, 14153 },
+ { -999, 16383 }
+};
+
+/* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/
+struct stb0899_tab stb0899_quant_tab[] = {
+ { 0, 0 },
+ { 0, 100 },
+ { 600, 200 },
+ { 950, 299 },
+ { 1200, 398 },
+ { 1400, 501 },
+ { 1560, 603 },
+ { 1690, 700 },
+ { 1810, 804 },
+ { 1910, 902 },
+ { 2000, 1000 },
+ { 2080, 1096 },
+ { 2160, 1202 },
+ { 2230, 1303 },
+ { 2350, 1496 },
+ { 2410, 1603 },
+ { 2460, 1698 },
+ { 2510, 1799 },
+ { 2600, 1995 },
+ { 2650, 2113 },
+ { 2690, 2213 },
+ { 2720, 2291 },
+ { 2760, 2399 },
+ { 2800, 2512 },
+ { 2860, 2692 },
+ { 2930, 2917 },
+ { 2960, 3020 },
+ { 3010, 3199 },
+ { 3040, 3311 },
+ { 3060, 3388 },
+ { 3120, 3631 },
+ { 3190, 3936 },
+ { 3400, 5012 },
+ { 3610, 6383 },
+ { 3800, 7943 },
+ { 4210, 12735 },
+ { 4500, 17783 },
+ { 4690, 22131 },
+ { 4810, 25410 }
+};
+
+/* DVB-S2 Es/N0 estimate in dB/100 vs read value */
+struct stb0899_tab stb0899_est_tab[] = {
+ { 0, 0 },
+ { 0, 1 },
+ { 301, 2 },
+ { 1204, 16 },
+ { 1806, 64 },
+ { 2408, 256 },
+ { 2709, 512 },
+ { 3010, 1023 },
+ { 3311, 2046 },
+ { 3612, 4093 },
+ { 3823, 6653 },
+ { 3913, 8185 },
+ { 4010, 10233 },
+ { 4107, 12794 },
+ { 4214, 16368 },
+ { 4266, 18450 },
+ { 4311, 20464 },
+ { 4353, 22542 },
+ { 4391, 24604 },
+ { 4425, 26607 },
+ { 4457, 28642 },
+ { 4487, 30690 },
+ { 4515, 32734 },
+ { 4612, 40926 },
+ { 4692, 49204 },
+ { 4816, 65464 },
+ { 4913, 81846 },
+ { 4993, 98401 },
+ { 5060, 114815 },
+ { 5118, 131220 },
+ { 5200, 158489 },
+ { 5300, 199526 },
+ { 5400, 251189 },
+ { 5500, 316228 },
+ { 5600, 398107 },
+ { 5720, 524807 },
+ { 5721, 526017 },
+};
+
+int _stb0899_read_reg(struct stb0899_state *state, unsigned int reg)
+{
+ int ret;
+
+ u8 b0[] = { reg >> 8, reg & 0xff };
+ u8 buf;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 2
+ },{
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = &buf,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret != 2) {
+ if (ret != -ERESTARTSYS)
+ dprintk(state->verbose, FE_ERROR, 1,
+ "Read error, Reg=[0x%02x], Status=%d",
+ reg, ret);
+
+ return ret < 0 ? ret : -EREMOTEIO;
+ }
+ if (unlikely(*state->verbose >= FE_DEBUGREG))
+ dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%02x], data=%02x",
+ reg, buf);
+
+ return (unsigned int)buf;
+}
+
+int stb0899_read_reg(struct stb0899_state *state, unsigned int reg)
+{
+ int result;
+
+ result = _stb0899_read_reg(state, reg);
+ /*
+ * Bug ID 9:
+ * access to 0xf2xx/0xf6xx
+ * must be followed by read from 0xf2ff/0xf6ff.
+ */
+ if ((reg != 0xf2ff) && (reg != 0xf6ff) &&
+ (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600)))
+ _stb0899_read_reg(state, (reg | 0x00ff));
+
+ return result;
+}
+
+u32 _stb0899_read_s2reg(struct stb0899_state *state,
+ u32 stb0899_i2cdev,
+ u32 stb0899_base_addr,
+ u16 stb0899_reg_offset)
+{
+ int status;
+ u32 data;
+ u8 buf[7] = { 0 };
+ u16 tmpaddr;
+
+ u8 buf_0[] = {
+ GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */
+ GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */
+ GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */
+ };
+ u8 buf_1[] = {
+ 0x00, /* 0xf3 Reg Offset */
+ 0x00, /* 0x44 Reg Offset */
+ };
+
+ struct i2c_msg msg_0 = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf_0,
+ .len = 6
+ };
+
+ struct i2c_msg msg_1 = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf_1,
+ .len = 2
+ };
+
+ struct i2c_msg msg_r = {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = 4
+ };
+
+ tmpaddr = stb0899_reg_offset & 0xff00;
+ if (!(stb0899_reg_offset & 0x8))
+ tmpaddr = stb0899_reg_offset | 0x20;
+
+ buf_1[0] = GETBYTE(tmpaddr, BYTE1);
+ buf_1[1] = GETBYTE(tmpaddr, BYTE0);
+
+ status = i2c_transfer(state->i2c, &msg_0, 1);
+ if (status < 1) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR(1), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status);
+
+ goto err;
+ }
+
+ /* Dummy */
+ status = i2c_transfer(state->i2c, &msg_1, 1);
+ if (status < 1)
+ goto err;
+
+ status = i2c_transfer(state->i2c, &msg_r, 1);
+ if (status < 1)
+ goto err;
+
+ buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1);
+ buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0);
+
+ /* Actual */
+ status = i2c_transfer(state->i2c, &msg_1, 1);
+ if (status < 1) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR(2), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status);
+ goto err;
+ }
+
+ status = i2c_transfer(state->i2c, &msg_r, 1);
+ if (status < 1) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR(3), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status);
+ return status < 0 ? status : -EREMOTEIO;
+ }
+
+ data = MAKEWORD32(buf[3], buf[2], buf[1], buf[0]);
+ if (unlikely(*state->verbose >= FE_DEBUGREG))
+ printk(KERN_DEBUG "%s Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, data);
+
+ return data;
+
+err:
+ return status < 0 ? status : -EREMOTEIO;
+}
+
+int stb0899_write_s2reg(struct stb0899_state *state,
+ u32 stb0899_i2cdev,
+ u32 stb0899_base_addr,
+ u16 stb0899_reg_offset,
+ u32 stb0899_data)
+{
+ int status;
+
+ /* Base Address Setup */
+ u8 buf_0[] = {
+ GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */
+ GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */
+ GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */
+ };
+ u8 buf_1[] = {
+ 0x00, /* 0xf3 Reg Offset */
+ 0x00, /* 0x44 Reg Offset */
+ 0x00, /* data */
+ 0x00, /* data */
+ 0x00, /* data */
+ 0x00, /* data */
+ };
+
+ struct i2c_msg msg_0 = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf_0,
+ .len = 6
+ };
+
+ struct i2c_msg msg_1 = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf_1,
+ .len = 6
+ };
+
+ buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1);
+ buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0);
+ buf_1[2] = GETBYTE(stb0899_data, BYTE0);
+ buf_1[3] = GETBYTE(stb0899_data, BYTE1);
+ buf_1[4] = GETBYTE(stb0899_data, BYTE2);
+ buf_1[5] = GETBYTE(stb0899_data, BYTE3);
+
+ if (unlikely(*state->verbose >= FE_DEBUGREG))
+ printk(KERN_DEBUG "%s Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data);
+
+ status = i2c_transfer(state->i2c, &msg_0, 1);
+ if (unlikely(status < 1)) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR (1), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status);
+ goto err;
+ }
+ status = i2c_transfer(state->i2c, &msg_1, 1);
+ if (unlikely(status < 1)) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR (2), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status);
+
+ return status < 0 ? status : -EREMOTEIO;
+ }
+
+ return 0;
+
+err:
+ return status < 0 ? status : -EREMOTEIO;
+}
+
+int stb0899_read_regs(struct stb0899_state *state, unsigned int reg, u8 *buf, u32 count)
+{
+ int status;
+
+ u8 b0[] = { reg >> 8, reg & 0xff };
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 2
+ },{
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = count
+ }
+ };
+
+ status = i2c_transfer(state->i2c, msg, 2);
+ if (status != 2) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s Read error, Reg=[0x%04x], Count=%u, Status=%d\n",
+ __func__, reg, count, status);
+ goto err;
+ }
+ /*
+ * Bug ID 9:
+ * access to 0xf2xx/0xf6xx
+ * must be followed by read from 0xf2ff/0xf6ff.
+ */
+ if ((reg != 0xf2ff) && (reg != 0xf6ff) &&
+ (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600)))
+ _stb0899_read_reg(state, (reg | 0x00ff));
+
+ if (unlikely(*state->verbose >= FE_DEBUGREG)) {
+ int i;
+
+ printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg);
+ for (i = 0; i < count; i++) {
+ printk(" %02x", buf[i]);
+ }
+ printk("\n");
+ }
+
+ return 0;
+err:
+ return status < 0 ? status : -EREMOTEIO;
+}
+
+int stb0899_write_regs(struct stb0899_state *state, unsigned int reg, u8 *data, u32 count)
+{
+ int ret;
+ u8 buf[2 + count];
+ struct i2c_msg i2c_msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 2 + count
+ };
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+ memcpy(&buf[2], data, count);
+
+ if (unlikely(*state->verbose >= FE_DEBUGREG)) {
+ int i;
+
+ printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg);
+ for (i = 0; i < count; i++)
+ printk(" %02x", data[i]);
+ printk("\n");
+ }
+ ret = i2c_transfer(state->i2c, &i2c_msg, 1);
+
+ /*
+ * Bug ID 9:
+ * access to 0xf2xx/0xf6xx
+ * must be followed by read from 0xf2ff/0xf6ff.
+ */
+ if ((((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600)))
+ stb0899_read_reg(state, (reg | 0x00ff));
+
+ if (ret != 1) {
+ if (ret != -ERESTARTSYS)
+ dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d",
+ reg, data[0], count, ret);
+ return ret < 0 ? ret : -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+int stb0899_write_reg(struct stb0899_state *state, unsigned int reg, u8 data)
+{
+ return stb0899_write_regs(state, reg, &data, 1);
+}
+
+/*
+ * stb0899_get_mclk
+ * Get STB0899 master clock frequency
+ * ExtClk: external clock frequency (Hz)
+ */
+static u32 stb0899_get_mclk(struct stb0899_state *state)
+{
+ u32 mclk = 0, div = 0;
+
+ div = stb0899_read_reg(state, STB0899_NCOARSE);
+ mclk = (div + 1) * state->config->xtal_freq / 6;
+ dprintk(state->verbose, FE_DEBUG, 1, "div=%d, mclk=%d", div, mclk);
+
+ return mclk;
+}
+
+/*
+ * stb0899_set_mclk
+ * Set STB0899 master Clock frequency
+ * Mclk: demodulator master clock
+ * ExtClk: external clock frequency (Hz)
+ */
+static void stb0899_set_mclk(struct stb0899_state *state, u32 Mclk)
+{
+ struct stb0899_internal *internal = &state->internal;
+ u8 mdiv = 0;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "state->config=%p", state->config);
+ mdiv = ((6 * Mclk) / state->config->xtal_freq) - 1;
+ dprintk(state->verbose, FE_DEBUG, 1, "mdiv=%d", mdiv);
+
+ stb0899_write_reg(state, STB0899_NCOARSE, mdiv);
+ internal->master_clk = stb0899_get_mclk(state);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "MasterCLOCK=%d", internal->master_clk);
+}
+
+static int stb0899_postproc(struct stb0899_state *state, u8 ctl, int enable)
+{
+ struct stb0899_config *config = state->config;
+ const struct stb0899_postproc *postproc = config->postproc;
+
+ /* post process event */
+ if (postproc) {
+ if (enable) {
+ if (postproc[ctl].level == STB0899_GPIOPULLUP)
+ stb0899_write_reg(state, postproc[ctl].gpio, 0x02);
+ else
+ stb0899_write_reg(state, postproc[ctl].gpio, 0x82);
+ } else {
+ if (postproc[ctl].level == STB0899_GPIOPULLUP)
+ stb0899_write_reg(state, postproc[ctl].gpio, 0x82);
+ else
+ stb0899_write_reg(state, postproc[ctl].gpio, 0x02);
+ }
+ }
+ return 0;
+}
+
+static void stb0899_release(struct dvb_frontend *fe)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "Release Frontend");
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0);
+ kfree(state);
+}
+
+/*
+ * stb0899_get_alpha
+ * return: rolloff
+ */
+static int stb0899_get_alpha(struct stb0899_state *state)
+{
+ u8 mode_coeff;
+
+ mode_coeff = stb0899_read_reg(state, STB0899_DEMOD);
+
+ if (STB0899_GETFIELD(MODECOEFF, mode_coeff) == 1)
+ return 20;
+ else
+ return 35;
+}
+
+/*
+ * stb0899_init_calc
+ */
+static void stb0899_init_calc(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ int master_clk;
+ u8 agc[2];
+ u8 agc1cn;
+ u32 reg;
+
+ /* Read registers (in burst mode) */
+ agc1cn = stb0899_read_reg(state, STB0899_AGC1CN);
+ stb0899_read_regs(state, STB0899_AGC1REF, agc, 2); /* AGC1R and AGC2O */
+
+ /* Initial calculations */
+ master_clk = stb0899_get_mclk(state);
+ internal->t_agc1 = 0;
+ internal->t_agc2 = 0;
+ internal->master_clk = master_clk;
+ internal->mclk = master_clk / 65536L;
+ internal->rolloff = stb0899_get_alpha(state);
+
+ /* DVBS2 Initial calculations */
+ /* Set AGC value to the middle */
+ internal->agc_gain = 8154;
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL);
+ STB0899_SETFIELD_VAL(IF_GAIN_INIT, reg, internal->agc_gain);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, RRC_ALPHA);
+ internal->rrc_alpha = STB0899_GETFIELD(RRC_ALPHA, reg);
+
+ internal->center_freq = 0;
+ internal->av_frame_coarse = 10;
+ internal->av_frame_fine = 20;
+ internal->step_size = 2;
+/*
+ if ((pParams->SpectralInv == FE_IQ_NORMAL) || (pParams->SpectralInv == FE_IQ_AUTO))
+ pParams->IQLocked = 0;
+ else
+ pParams->IQLocked = 1;
+*/
+}
+
+static int stb0899_wait_diseqc_fifo_empty(struct stb0899_state *state, int timeout)
+{
+ u8 reg = 0;
+ unsigned long start = jiffies;
+
+ while (1) {
+ reg = stb0899_read_reg(state, STB0899_DISSTATUS);
+ if (!STB0899_GETFIELD(FIFOFULL, reg))
+ break;
+ if ((jiffies - start) > timeout) {
+ dprintk(state->verbose, FE_ERROR, 1, "timed out !!");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int stb0899_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ u8 reg, i;
+
+ if (cmd->msg_len > 8)
+ return -EINVAL;
+
+ /* enable FIFO precharge */
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 1);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+ for (i = 0; i < cmd->msg_len; i++) {
+ /* wait for FIFO empty */
+ if (stb0899_wait_diseqc_fifo_empty(state, 10) < 0)
+ return -ETIMEDOUT;
+
+ stb0899_write_reg(state, STB0899_DISFIFO, cmd->msg[i]);
+ }
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+
+ return 0;
+}
+
+static int stb0899_wait_diseqc_rxidle(struct stb0899_state *state, int timeout)
+{
+ u8 reg = 0;
+ unsigned long start = jiffies;
+
+ while (!STB0899_GETFIELD(RXEND, reg)) {
+ reg = stb0899_read_reg(state, STB0899_DISRX_ST0);
+ if (jiffies - start > timeout) {
+ dprintk(state->verbose, FE_ERROR, 1, "timed out!!");
+ return -ETIMEDOUT;
+ }
+ msleep(10);
+ }
+
+ return 0;
+}
+
+static int stb0899_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ u8 reg, length = 0, i;
+ int result;
+
+ if (stb0899_wait_diseqc_rxidle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ reg = stb0899_read_reg(state, STB0899_DISRX_ST0);
+ if (STB0899_GETFIELD(RXEND, reg)) {
+
+ reg = stb0899_read_reg(state, STB0899_DISRX_ST1);
+ length = STB0899_GETFIELD(FIFOBYTENBR, reg);
+
+ if (length > sizeof (reply->msg)) {
+ result = -EOVERFLOW;
+ goto exit;
+ }
+ reply->msg_len = length;
+
+ /* extract data */
+ for (i = 0; i < length; i++)
+ reply->msg[i] = stb0899_read_reg(state, STB0899_DISFIFO);
+ }
+
+ return 0;
+exit:
+
+ return result;
+}
+
+static int stb0899_wait_diseqc_txidle(struct stb0899_state *state, int timeout)
+{
+ u8 reg = 0;
+ unsigned long start = jiffies;
+
+ while (!STB0899_GETFIELD(TXIDLE, reg)) {
+ reg = stb0899_read_reg(state, STB0899_DISSTATUS);
+ if (jiffies - start > timeout) {
+ dprintk(state->verbose, FE_ERROR, 1, "timed out!!");
+ return -ETIMEDOUT;
+ }
+ msleep(10);
+ }
+ return 0;
+}
+
+static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ u8 reg, old_state;
+
+ /* wait for diseqc idle */
+ if (stb0899_wait_diseqc_txidle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ old_state = reg;
+ /* set to burst mode */
+ STB0899_SETFIELD_VAL(DISEQCMODE, reg, 0x02);
+ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x01);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+ switch (burst) {
+ case SEC_MINI_A:
+ /* unmodulated */
+ stb0899_write_reg(state, STB0899_DISFIFO, 0x00);
+ break;
+ case SEC_MINI_B:
+ /* modulated */
+ stb0899_write_reg(state, STB0899_DISFIFO, 0xff);
+ break;
+ }
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x00);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+ /* wait for diseqc idle */
+ if (stb0899_wait_diseqc_txidle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ /* restore state */
+ stb0899_write_reg(state, STB0899_DISCNTRL1, old_state);
+
+ return 0;
+}
+
+static int stb0899_diseqc_init(struct stb0899_state *state)
+{
+ struct dvb_diseqc_master_cmd tx_data;
+/*
+ struct dvb_diseqc_slave_reply rx_data;
+*/
+ u8 f22_tx, f22_rx, reg;
+
+ u32 mclk, tx_freq = 22000;/* count = 0, i; */
+ tx_data.msg[0] = 0xe2;
+ tx_data.msg_len = 3;
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL2);
+ STB0899_SETFIELD_VAL(ONECHIP_TRX, reg, 0);
+ stb0899_write_reg(state, STB0899_DISCNTRL2, reg);
+
+ /* disable Tx spy */
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISEQCRESET, reg, 1);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISEQCRESET, reg, 0);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+
+ mclk = stb0899_get_mclk(state);
+ f22_tx = mclk / (tx_freq * 32);
+ stb0899_write_reg(state, STB0899_DISF22, f22_tx); /* DiSEqC Tx freq */
+ state->rx_freq = 20000;
+ f22_rx = mclk / (state->rx_freq * 32);
+
+ return 0;
+}
+
+static int stb0899_sleep(struct dvb_frontend *fe)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+/*
+ u8 reg;
+*/
+ dprintk(state->verbose, FE_DEBUG, 1, "Going to Sleep .. (Really tired .. :-))");
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0);
+
+ return 0;
+}
+
+static int stb0899_wakeup(struct dvb_frontend *fe)
+{
+ int rc;
+ struct stb0899_state *state = fe->demodulator_priv;
+
+ if ((rc = stb0899_write_reg(state, STB0899_SYNTCTRL, STB0899_SELOSCI)))
+ return rc;
+ /* Activate all clocks; DVB-S2 registers are inaccessible otherwise. */
+ if ((rc = stb0899_write_reg(state, STB0899_STOPCLK1, 0x00)))
+ return rc;
+ if ((rc = stb0899_write_reg(state, STB0899_STOPCLK2, 0x00)))
+ return rc;
+
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 1);
+
+ return 0;
+}
+
+static int stb0899_init(struct dvb_frontend *fe)
+{
+ int i;
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_config *config = state->config;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "Initializing STB0899 ... ");
+
+ /* init device */
+ dprintk(state->verbose, FE_DEBUG, 1, "init device");
+ for (i = 0; config->init_dev[i].address != 0xffff; i++)
+ stb0899_write_reg(state, config->init_dev[i].address, config->init_dev[i].data);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "init S2 demod");
+ /* init S2 demod */
+ for (i = 0; config->init_s2_demod[i].offset != 0xffff; i++)
+ stb0899_write_s2reg(state, STB0899_S2DEMOD,
+ config->init_s2_demod[i].base_address,
+ config->init_s2_demod[i].offset,
+ config->init_s2_demod[i].data);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "init S1 demod");
+ /* init S1 demod */
+ for (i = 0; config->init_s1_demod[i].address != 0xffff; i++)
+ stb0899_write_reg(state, config->init_s1_demod[i].address, config->init_s1_demod[i].data);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "init S2 FEC");
+ /* init S2 fec */
+ for (i = 0; config->init_s2_fec[i].offset != 0xffff; i++)
+ stb0899_write_s2reg(state, STB0899_S2FEC,
+ config->init_s2_fec[i].base_address,
+ config->init_s2_fec[i].offset,
+ config->init_s2_fec[i].data);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "init TST");
+ /* init test */
+ for (i = 0; config->init_tst[i].address != 0xffff; i++)
+ stb0899_write_reg(state, config->init_tst[i].address, config->init_tst[i].data);
+
+ stb0899_init_calc(state);
+ stb0899_diseqc_init(state);
+
+ return 0;
+}
+
+static int stb0899_table_lookup(const struct stb0899_tab *tab, int max, int val)
+{
+ int res = 0;
+ int min = 0, med;
+
+ if (val < tab[min].read)
+ res = tab[min].real;
+ else if (val >= tab[max].read)
+ res = tab[max].real;
+ else {
+ while ((max - min) > 1) {
+ med = (max + min) / 2;
+ if (val >= tab[min].read && val < tab[med].read)
+ max = med;
+ else
+ min = med;
+ }
+ res = ((val - tab[min].read) *
+ (tab[max].real - tab[min].real) /
+ (tab[max].read - tab[min].read)) +
+ tab[min].real;
+ }
+
+ return res;
+}
+
+static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ int val;
+ u32 reg;
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ if (internal->lock) {
+ reg = stb0899_read_reg(state, STB0899_VSTATUS);
+ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
+
+ reg = stb0899_read_reg(state, STB0899_AGCIQIN);
+ val = (s32)(s8)STB0899_GETFIELD(AGCIQVALUE, reg);
+
+ *strength = stb0899_table_lookup(stb0899_dvbsrf_tab, ARRAY_SIZE(stb0899_dvbsrf_tab) - 1, val);
+ *strength += 750;
+ dprintk(state->verbose, FE_DEBUG, 1, "AGCIQVALUE = 0x%02x, C = %d * 0.1 dBm",
+ val & 0xff, *strength);
+ }
+ }
+ break;
+ case SYS_DVBS2:
+ if (internal->lock) {
+ reg = STB0899_READ_S2REG(STB0899_DEMOD, IF_AGC_GAIN);
+ val = STB0899_GETFIELD(IF_AGC_GAIN, reg);
+
+ *strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val);
+ *strength += 750;
+ dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm",
+ val & 0x3fff, *strength);
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stb0899_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ unsigned int val, quant, quantn = -1, est, estn = -1;
+ u8 buf[2];
+ u32 reg;
+
+ reg = stb0899_read_reg(state, STB0899_VSTATUS);
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ if (internal->lock) {
+ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
+
+ stb0899_read_regs(state, STB0899_NIRM, buf, 2);
+ val = MAKEWORD16(buf[0], buf[1]);
+
+ *snr = stb0899_table_lookup(stb0899_cn_tab, ARRAY_SIZE(stb0899_cn_tab) - 1, val);
+ dprintk(state->verbose, FE_DEBUG, 1, "NIR = 0x%02x%02x = %u, C/N = %d * 0.1 dBm\n",
+ buf[0], buf[1], val, *snr);
+ }
+ }
+ break;
+ case SYS_DVBS2:
+ if (internal->lock) {
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1);
+ quant = STB0899_GETFIELD(UWP_ESN0_QUANT, reg);
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2);
+ est = STB0899_GETFIELD(ESN0_EST, reg);
+ if (est == 1)
+ val = 301; /* C/N = 30.1 dB */
+ else if (est == 2)
+ val = 270; /* C/N = 27.0 dB */
+ else {
+ /* quantn = 100 * log(quant^2) */
+ quantn = stb0899_table_lookup(stb0899_quant_tab, ARRAY_SIZE(stb0899_quant_tab) - 1, quant * 100);
+ /* estn = 100 * log(est) */
+ estn = stb0899_table_lookup(stb0899_est_tab, ARRAY_SIZE(stb0899_est_tab) - 1, est);
+ /* snr(dBm/10) = -10*(log(est)-log(quant^2)) => snr(dBm/10) = (100*log(quant^2)-100*log(est))/10 */
+ val = (quantn - estn) / 10;
+ }
+ *snr = val;
+ dprintk(state->verbose, FE_DEBUG, 1, "Es/N0 quant = %d (%d) estimate = %u (%d), C/N = %d * 0.1 dBm",
+ quant, quantn, est, estn, val);
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stb0899_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+ u8 reg;
+ *status = 0;
+
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S/DSS");
+ if (internal->lock) {
+ reg = stb0899_read_reg(state, STB0899_VSTATUS);
+ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
+ dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK");
+ *status |= FE_HAS_CARRIER | FE_HAS_LOCK;
+
+ reg = stb0899_read_reg(state, STB0899_PLPARM);
+ if (STB0899_GETFIELD(VITCURPUN, reg)) {
+ dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_VITERBI | FE_HAS_SYNC");
+ *status |= FE_HAS_VITERBI | FE_HAS_SYNC;
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1);
+ }
+ }
+ }
+ break;
+ case SYS_DVBS2:
+ dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S2");
+ if (internal->lock) {
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2);
+ if (STB0899_GETFIELD(UWP_LOCK, reg) && STB0899_GETFIELD(CSM_LOCK, reg)) {
+ *status |= FE_HAS_CARRIER;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "UWP & CSM Lock ! ---> DVB-S2 FE_HAS_CARRIER");
+
+ reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1);
+ if (STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg)) {
+ *status |= FE_HAS_LOCK;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "Packet Delineator Locked ! -----> DVB-S2 FE_HAS_LOCK");
+
+ }
+ if (STB0899_GETFIELD(CONTINUOUS_STREAM, reg)) {
+ *status |= FE_HAS_VITERBI;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "Packet Delineator found VITERBI ! -----> DVB-S2 FE_HAS_VITERBI");
+ }
+ if (STB0899_GETFIELD(ACCEPTED_STREAM, reg)) {
+ *status |= FE_HAS_SYNC;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "Packet Delineator found SYNC ! -----> DVB-S2 FE_HAS_SYNC");
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1);
+ }
+ }
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * stb0899_get_error
+ * viterbi error for DVB-S/DSS
+ * packet error for DVB-S2
+ * Bit Error Rate or Packet Error Rate * 10 ^ 7
+ */
+static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ u8 lsb, msb;
+ u32 i;
+
+ *ber = 0;
+
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ if (internal->lock) {
+ /* average 5 BER values */
+ for (i = 0; i < 5; i++) {
+ msleep(100);
+ lsb = stb0899_read_reg(state, STB0899_ECNT1L);
+ msb = stb0899_read_reg(state, STB0899_ECNT1M);
+ *ber += MAKEWORD16(msb, lsb);
+ }
+ *ber /= 5;
+ /* Viterbi Check */
+ if (STB0899_GETFIELD(VSTATUS_PRFVIT, internal->v_status)) {
+ /* Error Rate */
+ *ber *= 9766;
+ /* ber = ber * 10 ^ 7 */
+ *ber /= (-1 + (1 << (2 * STB0899_GETFIELD(NOE, internal->err_ctrl))));
+ *ber /= 8;
+ }
+ }
+ break;
+ case SYS_DVBS2:
+ if (internal->lock) {
+ /* Average 5 PER values */
+ for (i = 0; i < 5; i++) {
+ msleep(100);
+ lsb = stb0899_read_reg(state, STB0899_ECNT1L);
+ msb = stb0899_read_reg(state, STB0899_ECNT1M);
+ *ber += MAKEWORD16(msb, lsb);
+ }
+ /* ber = ber * 10 ^ 7 */
+ *ber *= 10000000;
+ *ber /= (-1 + (1 << (4 + 2 * STB0899_GETFIELD(NOE, internal->err_ctrl))));
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stb0899_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82);
+ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02);
+ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x00);
+ break;
+ case SEC_VOLTAGE_18:
+ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x02);
+ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02);
+ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82);
+ break;
+ case SEC_VOLTAGE_OFF:
+ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82);
+ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x82);
+ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stb0899_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ u8 div, reg;
+
+ /* wait for diseqc idle */
+ if (stb0899_wait_diseqc_txidle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ div = (internal->master_clk / 100) / 5632;
+ div = (div + 5) / 10;
+ stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x66);
+ reg = stb0899_read_reg(state, STB0899_ACRPRESC);
+ STB0899_SETFIELD_VAL(ACRPRESC, reg, 0x03);
+ stb0899_write_reg(state, STB0899_ACRPRESC, reg);
+ stb0899_write_reg(state, STB0899_ACRDIV1, div);
+ break;
+ case SEC_TONE_OFF:
+ stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x20);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ int i2c_stat;
+ struct stb0899_state *state = fe->demodulator_priv;
+
+ i2c_stat = stb0899_read_reg(state, STB0899_I2CRPT);
+ if (i2c_stat < 0)
+ goto err;
+
+ if (enable) {
+ dprintk(state->verbose, FE_DEBUG, 1, "Enabling I2C Repeater ...");
+ i2c_stat |= STB0899_I2CTON;
+ if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0)
+ goto err;
+ } else {
+ dprintk(state->verbose, FE_DEBUG, 1, "Disabling I2C Repeater ...");
+ i2c_stat &= ~STB0899_I2CTON;
+ if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0)
+ goto err;
+ }
+ return 0;
+err:
+ dprintk(state->verbose, FE_ERROR, 1, "I2C Repeater control failed");
+ return -EREMOTEIO;
+}
+
+
+static inline void CONVERT32(u32 x, char *str)
+{
+ *str++ = (x >> 24) & 0xff;
+ *str++ = (x >> 16) & 0xff;
+ *str++ = (x >> 8) & 0xff;
+ *str++ = (x >> 0) & 0xff;
+ *str = '\0';
+}
+
+int stb0899_get_dev_id(struct stb0899_state *state)
+{
+ u8 chip_id, release;
+ u16 id;
+ u32 demod_ver = 0, fec_ver = 0;
+ char demod_str[5] = { 0 };
+ char fec_str[5] = { 0 };
+
+ id = stb0899_read_reg(state, STB0899_DEV_ID);
+ dprintk(state->verbose, FE_DEBUG, 1, "ID reg=[0x%02x]", id);
+ chip_id = STB0899_GETFIELD(CHIP_ID, id);
+ release = STB0899_GETFIELD(CHIP_REL, id);
+
+ dprintk(state->verbose, FE_ERROR, 1, "Device ID=[%d], Release=[%d]",
+ chip_id, release);
+
+ CONVERT32(STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CORE_ID), (char *)&demod_str);
+
+ demod_ver = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_VERSION_ID);
+ dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver);
+ CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str);
+ fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG);
+ if (! (chip_id > 0)) {
+ dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899");
+
+ return -ENODEV;
+ }
+ dprintk(state->verbose, FE_ERROR, 1, "FEC Core ID=[%s], Version=[%d]", (char*) &fec_str, fec_ver);
+
+ return 0;
+}
+
+static void stb0899_set_delivery(struct stb0899_state *state)
+{
+ u8 reg;
+ u8 stop_clk[2];
+
+ stop_clk[0] = stb0899_read_reg(state, STB0899_STOPCLK1);
+ stop_clk[1] = stb0899_read_reg(state, STB0899_STOPCLK2);
+
+ switch (state->delsys) {
+ case SYS_DVBS:
+ dprintk(state->verbose, FE_DEBUG, 1, "Delivery System -- DVB-S");
+ /* FECM/Viterbi ON */
+ reg = stb0899_read_reg(state, STB0899_FECM);
+ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0);
+ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_FECM, reg);
+
+ stb0899_write_reg(state, STB0899_RSULC, 0xb1);
+ stb0899_write_reg(state, STB0899_TSULC, 0x40);
+ stb0899_write_reg(state, STB0899_RSLLC, 0x42);
+ stb0899_write_reg(state, STB0899_TSLPL, 0x12);
+
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESLDPC, reg, 1);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1);
+
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1);
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1);
+
+ STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1);
+ break;
+ case SYS_DVBS2:
+ /* FECM/Viterbi OFF */
+ reg = stb0899_read_reg(state, STB0899_FECM);
+ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0);
+ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 0);
+ stb0899_write_reg(state, STB0899_FECM, reg);
+
+ stb0899_write_reg(state, STB0899_RSULC, 0xb1);
+ stb0899_write_reg(state, STB0899_TSULC, 0x42);
+ stb0899_write_reg(state, STB0899_RSLLC, 0x40);
+ stb0899_write_reg(state, STB0899_TSLPL, 0x02);
+
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESLDPC, reg, 0);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 0);
+ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 0);
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 0);
+ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 0);
+ break;
+ case SYS_DSS:
+ /* FECM/Viterbi ON */
+ reg = stb0899_read_reg(state, STB0899_FECM);
+ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 1);
+ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_FECM, reg);
+
+ stb0899_write_reg(state, STB0899_RSULC, 0xa1);
+ stb0899_write_reg(state, STB0899_TSULC, 0x61);
+ stb0899_write_reg(state, STB0899_RSLLC, 0x42);
+
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESLDPC, reg, 1);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1);
+
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1);
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1);
+
+ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1);
+ break;
+ default:
+ dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system");
+ break;
+ }
+ STB0899_SETFIELD_VAL(STOP_CKADCI108, stop_clk[0], 0);
+ stb0899_write_regs(state, STB0899_STOPCLK1, stop_clk, 2);
+}
+
+/*
+ * stb0899_set_iterations
+ * set the LDPC iteration scale function
+ */
+static void stb0899_set_iterations(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+
+ s32 iter_scale;
+ u32 reg;
+
+ iter_scale = 17 * (internal->master_clk / 1000);
+ iter_scale += 410000;
+ iter_scale /= (internal->srate / 1000000);
+ iter_scale /= 1000;
+
+ if (iter_scale > config->ldpc_max_iter)
+ iter_scale = config->ldpc_max_iter;
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, MAX_ITER);
+ STB0899_SETFIELD_VAL(MAX_ITERATIONS, reg, iter_scale);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg);
+}
+
+static enum dvbfe_search stb0899_search(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_params *i_params = &state->params;
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+ struct dtv_frontend_properties *props = &fe->dtv_property_cache;
+
+ u32 SearchRange, gain;
+
+ i_params->freq = p->frequency;
+ i_params->srate = p->u.qpsk.symbol_rate;
+ state->delsys = props->delivery_system;
+ dprintk(state->verbose, FE_DEBUG, 1, "delivery system=%d", state->delsys);
+
+ SearchRange = 10000000;
+ dprintk(state->verbose, FE_DEBUG, 1, "Frequency=%d, Srate=%d", i_params->freq, i_params->srate);
+ /* checking Search Range is meaningless for a fixed 3 Mhz */
+ if (INRANGE(i_params->srate, 1000000, 45000000)) {
+ dprintk(state->verbose, FE_DEBUG, 1, "Parameters IN RANGE");
+ stb0899_set_delivery(state);
+
+ if (state->config->tuner_set_rfsiggain) {
+ if (internal->srate > 15000000)
+ gain = 8; /* 15Mb < srate < 45Mb, gain = 8dB */
+ else if (internal->srate > 5000000)
+ gain = 12; /* 5Mb < srate < 15Mb, gain = 12dB */
+ else
+ gain = 14; /* 1Mb < srate < 5Mb, gain = 14db */
+ state->config->tuner_set_rfsiggain(fe, gain);
+ }
+
+ if (i_params->srate <= 5000000)
+ stb0899_set_mclk(state, config->lo_clk);
+ else
+ stb0899_set_mclk(state, config->hi_clk);
+
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ dprintk(state->verbose, FE_DEBUG, 1, "DVB-S delivery system");
+ internal->freq = i_params->freq;
+ internal->srate = i_params->srate;
+ /*
+ * search = user search range +
+ * 500Khz +
+ * 2 * Tuner_step_size +
+ * 10% of the symbol rate
+ */
+ internal->srch_range = SearchRange + 1500000 + (i_params->srate / 5);
+ internal->derot_percent = 30;
+
+ /* What to do for tuners having no bandwidth setup ? */
+ /* enable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 1);
+
+ if (state->config->tuner_set_bandwidth)
+ state->config->tuner_set_bandwidth(fe, (13 * (stb0899_carr_width(state) + SearchRange)) / 10);
+ if (state->config->tuner_get_bandwidth)
+ state->config->tuner_get_bandwidth(fe, &internal->tuner_bw);
+
+ /* disable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 0);
+
+ /* Set DVB-S1 AGC */
+ stb0899_write_reg(state, STB0899_AGCRFCFG, 0x11);
+
+ /* Run the search algorithm */
+ dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S search algo ..");
+ if (stb0899_dvbs_algo(state) == RANGEOK) {
+ internal->lock = 1;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "-------------------------------------> DVB-S LOCK !");
+
+// stb0899_write_reg(state, STB0899_ERRCTRL1, 0x3d); /* Viterbi Errors */
+// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS);
+// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1);
+// dprintk(state->verbose, FE_DEBUG, 1, "VSTATUS=0x%02x", internal->v_status);
+// dprintk(state->verbose, FE_DEBUG, 1, "ERR_CTRL=0x%02x", internal->err_ctrl);
+
+ return DVBFE_ALGO_SEARCH_SUCCESS;
+ } else {
+ internal->lock = 0;
+
+ return DVBFE_ALGO_SEARCH_FAILED;
+ }
+ break;
+ case SYS_DVBS2:
+ internal->freq = i_params->freq;
+ internal->srate = i_params->srate;
+ internal->srch_range = SearchRange;
+
+ /* enable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 1);
+
+ if (state->config->tuner_set_bandwidth)
+ state->config->tuner_set_bandwidth(fe, (stb0899_carr_width(state) + SearchRange));
+ if (state->config->tuner_get_bandwidth)
+ state->config->tuner_get_bandwidth(fe, &internal->tuner_bw);
+
+ /* disable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 0);
+
+// pParams->SpectralInv = pSearch->IQ_Inversion;
+
+ /* Set DVB-S2 AGC */
+ stb0899_write_reg(state, STB0899_AGCRFCFG, 0x1c);
+
+ /* Set IterScale =f(MCLK,SYMB) */
+ stb0899_set_iterations(state);
+
+ /* Run the search algorithm */
+ dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S2 search algo ..");
+ if (stb0899_dvbs2_algo(state) == DVBS2_FEC_LOCK) {
+ internal->lock = 1;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "-------------------------------------> DVB-S2 LOCK !");
+
+// stb0899_write_reg(state, STB0899_ERRCTRL1, 0xb6); /* Packet Errors */
+// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS);
+// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1);
+
+ return DVBFE_ALGO_SEARCH_SUCCESS;
+ } else {
+ internal->lock = 0;
+
+ return DVBFE_ALGO_SEARCH_FAILED;
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system");
+ return DVBFE_ALGO_SEARCH_INVALID;
+ }
+ }
+
+ return DVBFE_ALGO_SEARCH_ERROR;
+}
+/*
+ * stb0899_track
+ * periodically check the signal level against a specified
+ * threshold level and perform derotator centering.
+ * called once we have a lock from a succesful search
+ * event.
+ *
+ * Will be called periodically called to maintain the
+ * lock.
+ *
+ * Will be used to get parameters as well as info from
+ * the decoded baseband header
+ *
+ * Once a new lock has established, the internal state
+ * frequency (internal->freq) is updated
+ */
+static int stb0899_track(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+ return 0;
+}
+
+static int stb0899_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "Get params");
+ p->u.qpsk.symbol_rate = internal->srate;
+
+ return 0;
+}
+
+static enum dvbfe_algo stb0899_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_CUSTOM;
+}
+
+static struct dvb_frontend_ops stb0899_ops = {
+
+ .info = {
+ .name = "STB0899 Multistandard",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 0,
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 5000000,
+ .symbol_rate_max = 45000000,
+
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK
+ },
+
+ .release = stb0899_release,
+ .init = stb0899_init,
+ .sleep = stb0899_sleep,
+// .wakeup = stb0899_wakeup,
+
+ .i2c_gate_ctrl = stb0899_i2c_gate_ctrl,
+
+ .get_frontend_algo = stb0899_frontend_algo,
+ .search = stb0899_search,
+ .track = stb0899_track,
+ .get_frontend = stb0899_get_frontend,
+
+
+ .read_status = stb0899_read_status,
+ .read_snr = stb0899_read_snr,
+ .read_signal_strength = stb0899_read_signal_strength,
+ .read_ber = stb0899_read_ber,
+
+ .set_voltage = stb0899_set_voltage,
+ .set_tone = stb0899_set_tone,
+
+ .diseqc_send_master_cmd = stb0899_send_diseqc_msg,
+ .diseqc_recv_slave_reply = stb0899_recv_slave_reply,
+ .diseqc_send_burst = stb0899_send_diseqc_burst,
+};
+
+struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_adapter *i2c)
+{
+ struct stb0899_state *state = NULL;
+ enum stb0899_inversion inversion;
+
+ state = kzalloc(sizeof (struct stb0899_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ inversion = config->inversion;
+ state->verbose = &verbose;
+ state->config = config;
+ state->i2c = i2c;
+ state->frontend.ops = stb0899_ops;
+ state->frontend.demodulator_priv = state;
+ state->internal.inversion = inversion;
+
+ stb0899_wakeup(&state->frontend);
+ if (stb0899_get_dev_id(state) == -ENODEV) {
+ printk("%s: Exiting .. !\n", __func__);
+ goto error;
+ }
+
+ printk("%s: Attaching STB0899 \n", __func__);
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(stb0899_attach);
+MODULE_PARM_DESC(verbose, "Set Verbosity level");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_DESCRIPTION("STB0899 Multi-Std frontend");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/stb0899_drv.h b/drivers/media/dvb/frontends/stb0899_drv.h
new file mode 100644
index 000000000000..98b200ce0c34
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb0899_drv.h
@@ -0,0 +1,162 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ 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 __STB0899_DRV_H
+#define __STB0899_DRV_H
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "dvb_frontend.h"
+
+#define STB0899_TSMODE_SERIAL 1
+#define STB0899_CLKPOL_FALLING 2
+#define STB0899_CLKNULL_PARITY 3
+#define STB0899_SYNC_FORCED 4
+#define STB0899_FECMODE_DSS 5
+
+struct stb0899_s1_reg {
+ u16 address;
+ u8 data;
+};
+
+struct stb0899_s2_reg {
+ u16 offset;
+ u32 base_address;
+ u32 data;
+};
+
+enum stb0899_inversion {
+ IQ_SWAP_OFF = 0,
+ IQ_SWAP_ON,
+ IQ_SWAP_AUTO
+};
+
+#define STB0899_GPIO00 0xf140
+#define STB0899_GPIO01 0xf141
+#define STB0899_GPIO02 0xf142
+#define STB0899_GPIO03 0xf143
+#define STB0899_GPIO04 0xf144
+#define STB0899_GPIO05 0xf145
+#define STB0899_GPIO06 0xf146
+#define STB0899_GPIO07 0xf147
+#define STB0899_GPIO08 0xf148
+#define STB0899_GPIO09 0xf149
+#define STB0899_GPIO10 0xf14a
+#define STB0899_GPIO11 0xf14b
+#define STB0899_GPIO12 0xf14c
+#define STB0899_GPIO13 0xf14d
+#define STB0899_GPIO14 0xf14e
+#define STB0899_GPIO15 0xf14f
+#define STB0899_GPIO16 0xf150
+#define STB0899_GPIO17 0xf151
+#define STB0899_GPIO18 0xf152
+#define STB0899_GPIO19 0xf153
+#define STB0899_GPIO20 0xf154
+
+#define STB0899_GPIOPULLUP 0x01 /* Output device is connected to Vdd */
+#define STB0899_GPIOPULLDN 0x00 /* Output device is connected to Vss */
+
+#define STB0899_POSTPROC_GPIO_POWER 0x00
+#define STB0899_POSTPROC_GPIO_LOCK 0x01
+
+/*
+ * Post process output configuration control
+ * 1. POWER ON/OFF (index 0)
+ * 2. FE_HAS_LOCK/LOCK_LOSS (index 1)
+ *
+ * @gpio = one of the above listed GPIO's
+ * @level = output state: pulled up or low
+ */
+struct stb0899_postproc {
+ u16 gpio;
+ u8 level;
+};
+
+struct stb0899_config {
+ const struct stb0899_s1_reg *init_dev;
+ const struct stb0899_s2_reg *init_s2_demod;
+ const struct stb0899_s1_reg *init_s1_demod;
+ const struct stb0899_s2_reg *init_s2_fec;
+ const struct stb0899_s1_reg *init_tst;
+
+ const struct stb0899_postproc *postproc;
+
+ enum stb0899_inversion inversion;
+
+ u32 xtal_freq;
+
+ u8 demod_address;
+ u8 ts_output_mode;
+ u8 block_sync_mode;
+ u8 ts_pfbit_toggle;
+
+ u8 clock_polarity;
+ u8 data_clk_parity;
+ u8 fec_mode;
+ u8 data_output_ctl;
+ u8 data_fifo_mode;
+ u8 out_rate_comp;
+ u8 i2c_repeater;
+// int inversion;
+ int lo_clk;
+ int hi_clk;
+
+ u32 esno_ave;
+ u32 esno_quant;
+ u32 avframes_coarse;
+ u32 avframes_fine;
+ u32 miss_threshold;
+ u32 uwp_threshold_acq;
+ u32 uwp_threshold_track;
+ u32 uwp_threshold_sof;
+ u32 sof_search_timeout;
+
+ u32 btr_nco_bits;
+ u32 btr_gain_shift_offset;
+ u32 crl_nco_bits;
+ u32 ldpc_max_iter;
+
+ int (*tuner_set_frequency)(struct dvb_frontend *fe, u32 frequency);
+ int (*tuner_get_frequency)(struct dvb_frontend *fe, u32 *frequency);
+ int (*tuner_set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
+ int (*tuner_get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
+ int (*tuner_set_rfsiggain)(struct dvb_frontend *fe, u32 rf_gain);
+};
+
+#if defined(CONFIG_DVB_STB0899) || (defined(CONFIG_DVB_STB0899_MODULE) && defined(MODULE))
+
+extern struct dvb_frontend *stb0899_attach(struct stb0899_config *config,
+ struct i2c_adapter *i2c);
+
+#else
+
+static inline struct dvb_frontend *stb0899_attach(struct stb0899_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+#endif //CONFIG_DVB_STB0899
+
+
+#endif
diff --git a/drivers/media/dvb/frontends/stb0899_priv.h b/drivers/media/dvb/frontends/stb0899_priv.h
new file mode 100644
index 000000000000..24619e3689db
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb0899_priv.h
@@ -0,0 +1,267 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ 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 __STB0899_PRIV_H
+#define __STB0899_PRIV_H
+
+#include "dvb_frontend.h"
+#include "stb0899_drv.h"
+
+#define FE_ERROR 0
+#define FE_NOTICE 1
+#define FE_INFO 2
+#define FE_DEBUG 3
+#define FE_DEBUGREG 4
+
+#define dprintk(x, y, z, format, arg...) do { \
+ if (z) { \
+ if ((*x > FE_ERROR) && (*x > y)) \
+ printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \
+ else if ((*x > FE_NOTICE) && (*x > y)) \
+ printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \
+ else if ((*x > FE_INFO) && (*x > y)) \
+ printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \
+ else if ((*x > FE_DEBUG) && (*x > y)) \
+ printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \
+ } else { \
+ if (*x > y) \
+ printk(format, ##arg); \
+ } \
+} while(0)
+
+#define INRANGE(val, x, y) (((x <= val) && (val <= y)) || \
+ ((y <= val) && (val <= x)) ? 1 : 0)
+
+#define BYTE0 0
+#define BYTE1 8
+#define BYTE2 16
+#define BYTE3 24
+
+#define GETBYTE(x, y) (((x) >> (y)) & 0xff)
+#define MAKEWORD32(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
+#define MAKEWORD16(a, b) (((a) << 8) | (b))
+
+#define MIN(x, y) ((x) <= (y) ? (x) : (y))
+#define MAX(x, y) ((x) >= (y) ? (x) : (y))
+#define ABS(x) ((x) >= 0 ? (x) : -(x))
+
+#define LSB(x) ((x & 0xff))
+#define MSB(y) ((y >> 8) & 0xff)
+
+
+#define STB0899_GETFIELD(bitf, val) ((val >> STB0899_OFFST_##bitf) & ((1 << STB0899_WIDTH_##bitf) - 1))
+
+
+#define STB0899_SETFIELD(mask, val, width, offset) (mask & (~(((1 << width) - 1) << \
+ offset))) | ((val & \
+ ((1 << width) - 1)) << offset)
+
+#define STB0899_SETFIELD_VAL(bitf, mask, val) (mask = (mask & (~(((1 << STB0899_WIDTH_##bitf) - 1) <<\
+ STB0899_OFFST_##bitf))) | \
+ (val << STB0899_OFFST_##bitf))
+
+
+enum stb0899_status {
+ NOAGC1 = 0,
+ AGC1OK,
+ NOTIMING,
+ ANALOGCARRIER,
+ TIMINGOK,
+ NOAGC2,
+ AGC2OK,
+ NOCARRIER,
+ CARRIEROK,
+ NODATA,
+ FALSELOCK,
+ DATAOK,
+ OUTOFRANGE,
+ RANGEOK,
+ DVBS2_DEMOD_LOCK,
+ DVBS2_DEMOD_NOLOCK,
+ DVBS2_FEC_LOCK,
+ DVBS2_FEC_NOLOCK
+};
+
+enum stb0899_modcod {
+ STB0899_DUMMY_PLF,
+ STB0899_QPSK_14,
+ STB0899_QPSK_13,
+ STB0899_QPSK_25,
+ STB0899_QPSK_12,
+ STB0899_QPSK_35,
+ STB0899_QPSK_23,
+ STB0899_QPSK_34,
+ STB0899_QPSK_45,
+ STB0899_QPSK_56,
+ STB0899_QPSK_89,
+ STB0899_QPSK_910,
+ STB0899_8PSK_35,
+ STB0899_8PSK_23,
+ STB0899_8PSK_34,
+ STB0899_8PSK_56,
+ STB0899_8PSK_89,
+ STB0899_8PSK_910,
+ STB0899_16APSK_23,
+ STB0899_16APSK_34,
+ STB0899_16APSK_45,
+ STB0899_16APSK_56,
+ STB0899_16APSK_89,
+ STB0899_16APSK_910,
+ STB0899_32APSK_34,
+ STB0899_32APSK_45,
+ STB0899_32APSK_56,
+ STB0899_32APSK_89,
+ STB0899_32APSK_910
+};
+
+enum stb0899_frame {
+ STB0899_LONG_FRAME,
+ STB0899_SHORT_FRAME
+};
+
+enum stb0899_alpha {
+ RRC_20,
+ RRC_25,
+ RRC_35
+};
+
+struct stb0899_tab {
+ s32 real;
+ s32 read;
+};
+
+enum stb0899_fec {
+ STB0899_FEC_1_2 = 13,
+ STB0899_FEC_2_3 = 18,
+ STB0899_FEC_3_4 = 21,
+ STB0899_FEC_5_6 = 24,
+ STB0899_FEC_6_7 = 25,
+ STB0899_FEC_7_8 = 26
+};
+
+struct stb0899_params {
+ u32 freq; /* Frequency */
+ u32 srate; /* Symbol rate */
+ enum fe_code_rate fecrate;
+};
+
+struct stb0899_internal {
+ u32 master_clk;
+ u32 freq; /* Demod internal Frequency */
+ u32 srate; /* Demod internal Symbol rate */
+ enum stb0899_fec fecrate; /* Demod internal FEC rate */
+ u32 srch_range; /* Demod internal Search Range */
+ u32 sub_range; /* Demod current sub range (Hz) */
+ u32 tuner_step; /* Tuner step (Hz) */
+ u32 tuner_offst; /* Relative offset to carrier (Hz) */
+ u32 tuner_bw; /* Current bandwidth of the tuner (Hz) */
+
+ s32 mclk; /* Masterclock Divider factor (binary) */
+ s32 rolloff; /* Current RollOff of the filter (x100) */
+
+ s16 derot_freq; /* Current derotator frequency (Hz) */
+ s16 derot_percent;
+
+ s16 direction; /* Current derotator search direction */
+ s16 derot_step; /* Derotator step (binary value) */
+ s16 t_derot; /* Derotator time constant (ms) */
+ s16 t_data; /* Data recovery time constant (ms) */
+ s16 sub_dir; /* Direction of the next sub range */
+
+ s16 t_agc1; /* Agc1 time constant (ms) */
+ s16 t_agc2; /* Agc2 time constant (ms) */
+
+ u32 lock; /* Demod internal lock state */
+ enum stb0899_status status; /* Demod internal status */
+
+ /* DVB-S2 */
+ s32 agc_gain; /* RF AGC Gain */
+ s32 center_freq; /* Nominal carrier frequency */
+ s32 av_frame_coarse; /* Coarse carrier freq search frames */
+ s32 av_frame_fine; /* Fine carrier freq search frames */
+
+ s16 step_size; /* Carrier frequency search step size */
+
+ enum stb0899_alpha rrc_alpha;
+ enum stb0899_inversion inversion;
+ enum stb0899_modcod modcod;
+ u8 pilots; /* Pilots found */
+
+ enum stb0899_frame frame_length;
+ u8 v_status; /* VSTATUS */
+ u8 err_ctrl; /* ERRCTRLn */
+};
+
+struct stb0899_state {
+ struct i2c_adapter *i2c;
+ struct stb0899_config *config;
+ struct dvb_frontend frontend;
+
+ u32 *verbose; /* Cached module verbosity level */
+
+ struct stb0899_internal internal; /* Device internal parameters */
+
+ /* cached params from API */
+ enum fe_delivery_system delsys;
+ struct stb0899_params params;
+
+ u32 rx_freq; /* DiSEqC 2.0 receiver freq */
+ struct mutex search_lock;
+};
+/* stb0899.c */
+extern int stb0899_read_reg(struct stb0899_state *state,
+ unsigned int reg);
+
+extern u32 _stb0899_read_s2reg(struct stb0899_state *state,
+ u32 stb0899_i2cdev,
+ u32 stb0899_base_addr,
+ u16 stb0899_reg_offset);
+
+extern int stb0899_read_regs(struct stb0899_state *state,
+ unsigned int reg, u8 *buf,
+ u32 count);
+
+extern int stb0899_write_regs(struct stb0899_state *state,
+ unsigned int reg, u8 *data,
+ u32 count);
+
+extern int stb0899_write_reg(struct stb0899_state *state,
+ unsigned int reg,
+ u8 data);
+
+extern int stb0899_write_s2reg(struct stb0899_state *state,
+ u32 stb0899_i2cdev,
+ u32 stb0899_base_addr,
+ u16 stb0899_reg_offset,
+ u32 stb0899_data);
+
+extern int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable);
+
+
+#define STB0899_READ_S2REG(DEVICE, REG) (_stb0899_read_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG))
+//#define STB0899_WRITE_S2REG(DEVICE, REG, DATA) (_stb0899_write_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG, DATA))
+
+/* stb0899_algo.c */
+extern enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state);
+extern enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state);
+extern long stb0899_carr_width(struct stb0899_state *state);
+
+#endif //__STB0899_PRIV_H
diff --git a/drivers/media/dvb/frontends/stb0899_reg.h b/drivers/media/dvb/frontends/stb0899_reg.h
new file mode 100644
index 000000000000..ba1ed56304a0
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb0899_reg.h
@@ -0,0 +1,2027 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ 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 __STB0899_REG_H
+#define __STB0899_REG_H
+
+/* S1 */
+#define STB0899_DEV_ID 0xf000
+#define STB0899_CHIP_ID (0x0f << 4)
+#define STB0899_OFFST_CHIP_ID 4
+#define STB0899_WIDTH_CHIP_ID 4
+#define STB0899_CHIP_REL (0x0f << 0)
+#define STB0899_OFFST_CHIP_REL 0
+#define STB0899_WIDTH_CHIP_REL 4
+
+#define STB0899_DEMOD 0xf40e
+#define STB0899_MODECOEFF (0x01 << 0)
+#define STB0899_OFFST_MODECOEFF 0
+#define STB0899_WIDTH_MODECOEFF 1
+
+#define STB0899_RCOMPC 0xf410
+#define STB0899_AGC1CN 0xf412
+#define STB0899_AGC1REF 0xf413
+#define STB0899_RTC 0xf417
+#define STB0899_TMGCFG 0xf418
+#define STB0899_AGC2REF 0xf419
+#define STB0899_TLSR 0xf41a
+
+#define STB0899_CFD 0xf41b
+#define STB0899_CFD_ON (0x01 << 7)
+#define STB0899_OFFST_CFD_ON 7
+#define STB0899_WIDTH_CFD_ON 1
+
+#define STB0899_ACLC 0xf41c
+
+#define STB0899_BCLC 0xf41d
+#define STB0899_OFFST_ALGO 6
+#define STB0899_WIDTH_ALGO_QPSK2 2
+#define STB0899_ALGO_QPSK2 (2 << 6)
+#define STB0899_ALGO_QPSK1 (1 << 6)
+#define STB0899_ALGO_BPSK (0 << 6)
+#define STB0899_OFFST_BETA 0
+#define STB0899_WIDTH_BETA 6
+
+#define STB0899_EQON 0xf41e
+#define STB0899_LDT 0xf41f
+#define STB0899_LDT2 0xf420
+#define STB0899_EQUALREF 0xf425
+#define STB0899_TMGRAMP 0xf426
+#define STB0899_TMGTHD 0xf427
+#define STB0899_IDCCOMP 0xf428
+#define STB0899_QDCCOMP 0xf429
+#define STB0899_POWERI 0xf42a
+#define STB0899_POWERQ 0xf42b
+#define STB0899_RCOMP 0xf42c
+
+#define STB0899_AGCIQIN 0xf42e
+#define STB0899_AGCIQVALUE (0xff << 0)
+#define STB0899_OFFST_AGCIQVALUE 0
+#define STB0899_WIDTH_AGCIQVALUE 8
+
+#define STB0899_AGC2I1 0xf436
+#define STB0899_AGC2I2 0xf437
+
+#define STB0899_TLIR 0xf438
+#define STB0899_TLIR_TMG_LOCK_IND (0xff << 0)
+#define STB0899_OFFST_TLIR_TMG_LOCK_IND 0
+#define STB0899_WIDTH_TLIR_TMG_LOCK_IND 8
+
+#define STB0899_RTF 0xf439
+#define STB0899_RTF_TIMING_LOOP_FREQ (0xff << 0)
+#define STB0899_OFFST_RTF_TIMING_LOOP_FREQ 0
+#define STB0899_WIDTH_RTF_TIMING_LOOP_FREQ 8
+
+#define STB0899_DSTATUS 0xf43a
+#define STB0899_CARRIER_FOUND (0x01 << 7)
+#define STB0899_OFFST_CARRIER_FOUND 7
+#define STB0899_WIDTH_CARRIER_FOUND 1
+#define STB0899_TMG_LOCK (0x01 << 6)
+#define STB0899_OFFST_TMG_LOCK 6
+#define STB0899_WIDTH_TMG_LOCK 1
+#define STB0899_DEMOD_LOCK (0x01 << 5)
+#define STB0899_OFFST_DEMOD_LOCK 5
+#define STB0899_WIDTH_DEMOD_LOCK 1
+#define STB0899_TMG_AUTO (0x01 << 4)
+#define STB0899_OFFST_TMG_AUTO 4
+#define STB0899_WIDTH_TMG_AUTO 1
+#define STB0899_END_MAIN (0x01 << 3)
+#define STB0899_OFFST_END_MAIN 3
+#define STB0899_WIDTH_END_MAIN 1
+
+#define STB0899_LDI 0xf43b
+#define STB0899_OFFST_LDI 0
+#define STB0899_WIDTH_LDI 8
+
+#define STB0899_CFRM 0xf43e
+#define STB0899_OFFST_CFRM 0
+#define STB0899_WIDTH_CFRM 8
+
+#define STB0899_CFRL 0xf43f
+#define STB0899_OFFST_CFRL 0
+#define STB0899_WIDTH_CFRL 8
+
+#define STB0899_NIRM 0xf440
+#define STB0899_OFFST_NIRM 0
+#define STB0899_WIDTH_NIRM 8
+
+#define STB0899_NIRL 0xf441
+#define STB0899_OFFST_NIRL 0
+#define STB0899_WIDTH_NIRL 8
+
+#define STB0899_ISYMB 0xf444
+#define STB0899_QSYMB 0xf445
+
+#define STB0899_SFRH 0xf446
+#define STB0899_OFFST_SFRH 0
+#define STB0899_WIDTH_SFRH 8
+
+#define STB0899_SFRM 0xf447
+#define STB0899_OFFST_SFRM 0
+#define STB0899_WIDTH_SFRM 8
+
+#define STB0899_SFRL 0xf448
+#define STB0899_OFFST_SFRL 4
+#define STB0899_WIDTH_SFRL 4
+
+#define STB0899_SFRUPH 0xf44c
+#define STB0899_SFRUPM 0xf44d
+#define STB0899_SFRUPL 0xf44e
+
+#define STB0899_EQUAI1 0xf4e0
+#define STB0899_EQUAQ1 0xf4e1
+#define STB0899_EQUAI2 0xf4e2
+#define STB0899_EQUAQ2 0xf4e3
+#define STB0899_EQUAI3 0xf4e4
+#define STB0899_EQUAQ3 0xf4e5
+#define STB0899_EQUAI4 0xf4e6
+#define STB0899_EQUAQ4 0xf4e7
+#define STB0899_EQUAI5 0xf4e8
+#define STB0899_EQUAQ5 0xf4e9
+
+#define STB0899_DSTATUS2 0xf50c
+#define STB0899_DS2_TMG_AUTOSRCH (0x01 << 7)
+#define STB8999_OFFST_DS2_TMG_AUTOSRCH 7
+#define STB0899_WIDTH_DS2_TMG_AUTOSRCH 1
+#define STB0899_DS2_END_MAINLOOP (0x01 << 6)
+#define STB0899_OFFST_DS2_END_MAINLOOP 6
+#define STB0899_WIDTH_DS2_END_MAINLOOP 1
+#define STB0899_DS2_CFSYNC (0x01 << 5)
+#define STB0899_OFFST_DS2_CFSYNC 5
+#define STB0899_WIDTH_DS2_CFSYNC 1
+#define STB0899_DS2_TMGLOCK (0x01 << 4)
+#define STB0899_OFFST_DS2_TMGLOCK 4
+#define STB0899_WIDTH_DS2_TMGLOCK 1
+#define STB0899_DS2_DEMODWAIT (0x01 << 3)
+#define STB0899_OFFST_DS2_DEMODWAIT 3
+#define STB0899_WIDTH_DS2_DEMODWAIT 1
+#define STB0899_DS2_FECON (0x01 << 1)
+#define STB0899_OFFST_DS2_FECON 1
+#define STB0899_WIDTH_DS2_FECON 1
+
+/* S1 FEC */
+#define STB0899_VSTATUS 0xf50d
+#define STB0899_VSTATUS_VITERBI_ON (0x01 << 7)
+#define STB0899_OFFST_VSTATUS_VITERBI_ON 7
+#define STB0899_WIDTH_VSTATUS_VITERBI_ON 1
+#define STB0899_VSTATUS_END_LOOPVIT (0x01 << 6)
+#define STB0899_OFFST_VSTATUS_END_LOOPVIT 6
+#define STB0899_WIDTH_VSTATUS_END_LOOPVIT 1
+#define STB0899_VSTATUS_PRFVIT (0x01 << 4)
+#define STB0899_OFFST_VSTATUS_PRFVIT 4
+#define STB0899_WIDTH_VSTATUS_PRFVIT 1
+#define STB0899_VSTATUS_LOCKEDVIT (0x01 << 3)
+#define STB0899_OFFST_VSTATUS_LOCKEDVIT 3
+#define STB0899_WIDTH_VSTATUS_LOCKEDVIT 1
+
+#define STB0899_VERROR 0xf50f
+
+#define STB0899_IQSWAP 0xf523
+#define STB0899_SYM (0x01 << 3)
+#define STB0899_OFFST_SYM 3
+#define STB0899_WIDTH_SYM 1
+
+#define STB0899_FECAUTO1 0xf530
+#define STB0899_DSSSRCH (0x01 << 3)
+#define STB0899_OFFST_DSSSRCH 3
+#define STB0899_WIDTH_DSSSRCH 1
+#define STB0899_SYMSRCH (0x01 << 2)
+#define STB0899_OFFST_SYMSRCH 2
+#define STB0899_WIDTH_SYMSRCH 1
+#define STB0899_QPSKSRCH (0x01 << 1)
+#define STB0899_OFFST_QPSKSRCH 1
+#define STB0899_WIDTH_QPSKSRCH 1
+#define STB0899_BPSKSRCH (0x01 << 0)
+#define STB0899_OFFST_BPSKSRCH 0
+#define STB0899_WIDTH_BPSKSRCH 1
+
+#define STB0899_FECM 0xf533
+#define STB0899_FECM_NOT_DVB (0x01 << 7)
+#define STB0899_OFFST_FECM_NOT_DVB 7
+#define STB0899_WIDTH_FECM_NOT_DVB 1
+#define STB0899_FECM_RSVD1 (0x07 << 4)
+#define STB0899_OFFST_FECM_RSVD1 4
+#define STB0899_WIDTH_FECM_RSVD1 3
+#define STB0899_FECM_VITERBI_ON (0x01 << 3)
+#define STB0899_OFFST_FECM_VITERBI_ON 3
+#define STB0899_WIDTH_FECM_VITERBI_ON 1
+#define STB0899_FECM_RSVD0 (0x01 << 2)
+#define STB0899_OFFST_FECM_RSVD0 2
+#define STB0899_WIDTH_FECM_RSVD0 1
+#define STB0899_FECM_SYNCDIS (0x01 << 1)
+#define STB0899_OFFST_FECM_SYNCDIS 1
+#define STB0899_WIDTH_FECM_SYNCDIS 1
+#define STB0899_FECM_SYMI (0x01 << 0)
+#define STB0899_OFFST_FECM_SYMI 0
+#define STB0899_WIDTH_FECM_SYMI 1
+
+#define STB0899_VTH12 0xf534
+#define STB0899_VTH23 0xf535
+#define STB0899_VTH34 0xf536
+#define STB0899_VTH56 0xf537
+#define STB0899_VTH67 0xf538
+#define STB0899_VTH78 0xf539
+
+#define STB0899_PRVIT 0xf53c
+#define STB0899_PR_7_8 (0x01 << 5)
+#define STB0899_OFFST_PR_7_8 5
+#define STB0899_WIDTH_PR_7_8 1
+#define STB0899_PR_6_7 (0x01 << 4)
+#define STB0899_OFFST_PR_6_7 4
+#define STB0899_WIDTH_PR_6_7 1
+#define STB0899_PR_5_6 (0x01 << 3)
+#define STB0899_OFFST_PR_5_6 3
+#define STB0899_WIDTH_PR_5_6 1
+#define STB0899_PR_3_4 (0x01 << 2)
+#define STB0899_OFFST_PR_3_4 2
+#define STB0899_WIDTH_PR_3_4 1
+#define STB0899_PR_2_3 (0x01 << 1)
+#define STB0899_OFFST_PR_2_3 1
+#define STB0899_WIDTH_PR_2_3 1
+#define STB0899_PR_1_2 (0x01 << 0)
+#define STB0899_OFFST_PR_1_2 0
+#define STB0899_WIDTH_PR_1_2 1
+
+#define STB0899_VITSYNC 0xf53d
+#define STB0899_AM (0x01 << 7)
+#define STB0899_OFFST_AM 7
+#define STB0899_WIDTH_AM 1
+#define STB0899_FREEZE (0x01 << 6)
+#define STB0899_OFFST_FREEZE 6
+#define STB0899_WIDTH_FREEZE 1
+#define STB0899_SN_65536 (0x03 << 4)
+#define STB0899_OFFST_SN_65536 4
+#define STB0899_WIDTH_SN_65536 2
+#define STB0899_SN_16384 (0x01 << 5)
+#define STB0899_OFFST_SN_16384 5
+#define STB0899_WIDTH_SN_16384 1
+#define STB0899_SN_4096 (0x01 << 4)
+#define STB0899_OFFST_SN_4096 4
+#define STB0899_WIDTH_SN_4096 1
+#define STB0899_SN_1024 (0x00 << 4)
+#define STB0899_OFFST_SN_1024 4
+#define STB0899_WIDTH_SN_1024 0
+#define STB0899_TO_128 (0x03 << 2)
+#define STB0899_OFFST_TO_128 2
+#define STB0899_WIDTH_TO_128 2
+#define STB0899_TO_64 (0x01 << 3)
+#define STB0899_OFFST_TO_64 3
+#define STB0899_WIDTH_TO_64 1
+#define STB0899_TO_32 (0x01 << 2)
+#define STB0899_OFFST_TO_32 2
+#define STB0899_WIDTH_TO_32 1
+#define STB0899_TO_16 (0x00 << 2)
+#define STB0899_OFFST_TO_16 2
+#define STB0899_WIDTH_TO_16 0
+#define STB0899_HYST_128 (0x03 << 1)
+#define STB0899_OFFST_HYST_128 1
+#define STB0899_WIDTH_HYST_128 2
+#define STB0899_HYST_64 (0x01 << 1)
+#define STB0899_OFFST_HYST_64 1
+#define STB0899_WIDTH_HYST_64 1
+#define STB0899_HYST_32 (0x01 << 0)
+#define STB0899_OFFST_HYST_32 0
+#define STB0899_WIDTH_HYST_32 1
+#define STB0899_HYST_16 (0x00 << 0)
+#define STB0899_OFFST_HYST_16 0
+#define STB0899_WIDTH_HYST_16 0
+
+#define STB0899_RSULC 0xf548
+#define STB0899_ULDIL_ON (0x01 << 7)
+#define STB0899_OFFST_ULDIL_ON 7
+#define STB0899_WIDTH_ULDIL_ON 1
+#define STB0899_ULAUTO_ON (0x01 << 6)
+#define STB0899_OFFST_ULAUTO_ON 6
+#define STB0899_WIDTH_ULAUTO_ON 1
+#define STB0899_ULRS_ON (0x01 << 5)
+#define STB0899_OFFST_ULRS_ON 5
+#define STB0899_WIDTH_ULRS_ON 1
+#define STB0899_ULDESCRAM_ON (0x01 << 4)
+#define STB0899_OFFST_ULDESCRAM_ON 4
+#define STB0899_WIDTH_ULDESCRAM_ON 1
+#define STB0899_UL_DISABLE (0x01 << 2)
+#define STB0899_OFFST_UL_DISABLE 2
+#define STB0899_WIDTH_UL_DISABLE 1
+#define STB0899_NOFTHRESHOLD (0x01 << 0)
+#define STB0899_OFFST_NOFTHRESHOLD 0
+#define STB0899_WIDTH_NOFTHRESHOLD 1
+
+#define STB0899_RSLLC 0xf54a
+#define STB0899_DEMAPVIT 0xf583
+#define STB0899_DEMAPVIT_RSVD (0x01 << 7)
+#define STB0899_OFFST_DEMAPVIT_RSVD 7
+#define STB0899_WIDTH_DEMAPVIT_RSVD 1
+#define STB0899_DEMAPVIT_KDIVIDER (0x7f << 0)
+#define STB0899_OFFST_DEMAPVIT_KDIVIDER 0
+#define STB0899_WIDTH_DEMAPVIT_KDIVIDER 7
+
+#define STB0899_PLPARM 0xf58c
+#define STB0899_VITMAPPING (0x07 << 5)
+#define STB0899_OFFST_VITMAPPING 5
+#define STB0899_WIDTH_VITMAPPING 3
+#define STB0899_VITMAPPING_BPSK (0x01 << 5)
+#define STB0899_OFFST_VITMAPPING_BPSK 5
+#define STB0899_WIDTH_VITMAPPING_BPSK 1
+#define STB0899_VITMAPPING_QPSK (0x00 << 5)
+#define STB0899_OFFST_VITMAPPING_QPSK 5
+#define STB0899_WIDTH_VITMAPPING_QPSK 0
+#define STB0899_VITCURPUN (0x1f << 0)
+#define STB0899_OFFST_VITCURPUN 0
+#define STB0899_WIDTH_VITCURPUN 5
+#define STB0899_VITCURPUN_1_2 (0x0d << 0)
+#define STB0899_VITCURPUN_2_3 (0x12 << 0)
+#define STB0899_VITCURPUN_3_4 (0x15 << 0)
+#define STB0899_VITCURPUN_5_6 (0x18 << 0)
+#define STB0899_VITCURPUN_6_7 (0x19 << 0)
+#define STB0899_VITCURPUN_7_8 (0x1a << 0)
+
+/* S2 DEMOD */
+#define STB0899_OFF0_DMD_STATUS 0xf300
+#define STB0899_BASE_DMD_STATUS 0x00000000
+#define STB0899_IF_AGC_LOCK (0x01 << 8)
+#define STB0899_OFFST_IF_AGC_LOCK 0
+#define STB0899_WIDTH_IF_AGC_LOCK 1
+
+#define STB0899_OFF0_CRL_FREQ 0xf304
+#define STB0899_BASE_CRL_FREQ 0x00000000
+#define STB0899_CARR_FREQ (0x3fffffff << 0)
+#define STB0899_OFFST_CARR_FREQ 0
+#define STB0899_WIDTH_CARR_FREQ 30
+
+#define STB0899_OFF0_BTR_FREQ 0xf308
+#define STB0899_BASE_BTR_FREQ 0x00000000
+#define STB0899_BTR_FREQ (0xfffffff << 0)
+#define STB0899_OFFST_BTR_FREQ 0
+#define STB0899_WIDTH_BTR_FREQ 28
+
+#define STB0899_OFF0_IF_AGC_GAIN 0xf30c
+#define STB0899_BASE_IF_AGC_GAIN 0x00000000
+#define STB0899_IF_AGC_GAIN (0x3fff < 0)
+#define STB0899_OFFST_IF_AGC_GAIN 0
+#define STB0899_WIDTH_IF_AGC_GAIN 14
+
+#define STB0899_OFF0_BB_AGC_GAIN 0xf310
+#define STB0899_BASE_BB_AGC_GAIN 0x00000000
+#define STB0899_BB_AGC_GAIN (0x3fff < 0)
+#define STB0899_OFFST_BB_AGC_GAIN 0
+#define STB0899_WIDTH_BB_AGC_GAIN 14
+
+#define STB0899_OFF0_DC_OFFSET 0xf314
+#define STB0899_BASE_DC_OFFSET 0x00000000
+#define STB0899_I (0xff < 8)
+#define STB0899_OFFST_I 8
+#define STB0899_WIDTH_I 8
+#define STB0899_Q (0xff < 0)
+#define STB0899_OFFST_Q 8
+#define STB0899_WIDTH_Q 8
+
+#define STB0899_OFF0_DMD_CNTRL 0xf31c
+#define STB0899_BASE_DMD_CNTRL 0x00000000
+#define STB0899_ADC0_PINS1IN (0x01 << 6)
+#define STB0899_OFFST_ADC0_PINS1IN 6
+#define STB0899_WIDTH_ADC0_PINS1IN 1
+#define STB0899_IN2COMP1_OFFBIN0 (0x01 << 3)
+#define STB0899_OFFST_IN2COMP1_OFFBIN0 3
+#define STB0899_WIDTH_IN2COMP1_OFFBIN0 1
+#define STB0899_DC_COMP (0x01 << 2)
+#define STB0899_OFFST_DC_COMP 2
+#define STB0899_WIDTH_DC_COMP 1
+#define STB0899_MODMODE (0x03 << 0)
+#define STB0899_OFFST_MODMODE 0
+#define STB0899_WIDTH_MODMODE 2
+
+#define STB0899_OFF0_IF_AGC_CNTRL 0xf320
+#define STB0899_BASE_IF_AGC_CNTRL 0x00000000
+#define STB0899_IF_GAIN_INIT (0x3fff << 13)
+#define STB0899_OFFST_IF_GAIN_INIT 13
+#define STB0899_WIDTH_IF_GAIN_INIT 14
+#define STB0899_IF_GAIN_SENSE (0x01 << 12)
+#define STB0899_OFFST_IF_GAIN_SENSE 12
+#define STB0899_WIDTH_IF_GAIN_SENSE 1
+#define STB0899_IF_LOOP_GAIN (0x0f << 8)
+#define STB0899_OFFST_IF_LOOP_GAIN 8
+#define STB0899_WIDTH_IF_LOOP_GAIN 4
+#define STB0899_IF_LD_GAIN_INIT (0x01 << 7)
+#define STB0899_OFFST_IF_LD_GAIN_INIT 7
+#define STB0899_WIDTH_IF_LD_GAIN_INIT 1
+#define STB0899_IF_AGC_REF (0x7f << 0)
+#define STB0899_OFFST_IF_AGC_REF 0
+#define STB0899_WIDTH_IF_AGC_REF 7
+
+#define STB0899_OFF0_BB_AGC_CNTRL 0xf324
+#define STB0899_BASE_BB_AGC_CNTRL 0x00000000
+#define STB0899_BB_GAIN_INIT (0x3fff << 12)
+#define STB0899_OFFST_BB_GAIN_INIT 12
+#define STB0899_WIDTH_BB_GAIN_INIT 14
+#define STB0899_BB_LOOP_GAIN (0x0f << 8)
+#define STB0899_OFFST_BB_LOOP_GAIN 8
+#define STB0899_WIDTH_BB_LOOP_GAIN 4
+#define STB0899_BB_LD_GAIN_INIT (0x01 << 7)
+#define STB0899_OFFST_BB_LD_GAIN_INIT 7
+#define STB0899_WIDTH_BB_LD_GAIN_INIT 1
+#define STB0899_BB_AGC_REF (0x7f << 0)
+#define STB0899_OFFST_BB_AGC_REF 0
+#define STB0899_WIDTH_BB_AGC_REF 7
+
+#define STB0899_OFF0_CRL_CNTRL 0xf328
+#define STB0899_BASE_CRL_CNTRL 0x00000000
+#define STB0899_CRL_LOCK_CLEAR (0x01 << 5)
+#define STB0899_OFFST_CRL_LOCK_CLEAR 5
+#define STB0899_WIDTH_CRL_LOCK_CLEAR 1
+#define STB0899_CRL_SWPR_CLEAR (0x01 << 4)
+#define STB0899_OFFST_CRL_SWPR_CLEAR 4
+#define STB0899_WIDTH_CRL_SWPR_CLEAR 1
+#define STB0899_CRL_SWP_ENA (0x01 << 3)
+#define STB0899_OFFST_CRL_SWP_ENA 3
+#define STB0899_WIDTH_CRL_SWP_ENA 1
+#define STB0899_CRL_DET_SEL (0x01 << 2)
+#define STB0899_OFFST_CRL_DET_SEL 2
+#define STB0899_WIDTH_CRL_DET_SEL 1
+#define STB0899_CRL_SENSE (0x01 << 1)
+#define STB0899_OFFST_CRL_SENSE 1
+#define STB0899_WIDTH_CRL_SENSE 1
+#define STB0899_CRL_PHSERR_CLEAR (0x01 << 0)
+#define STB0899_OFFST_CRL_PHSERR_CLEAR 0
+#define STB0899_WIDTH_CRL_PHSERR_CLEAR 1
+
+#define STB0899_OFF0_CRL_PHS_INIT 0xf32c
+#define STB0899_BASE_CRL_PHS_INIT 0x00000000
+#define STB0899_CRL_PHS_INIT_31 (0x1 << 30)
+#define STB0899_OFFST_CRL_PHS_INIT_31 30
+#define STB0899_WIDTH_CRL_PHS_INIT_31 1
+#define STB0899_CRL_LD_INIT_PHASE (0x1 << 24)
+#define STB0899_OFFST_CRL_LD_INIT_PHASE 24
+#define STB0899_WIDTH_CRL_LD_INIT_PHASE 1
+#define STB0899_CRL_INIT_PHASE (0xffffff << 0)
+#define STB0899_OFFST_CRL_INIT_PHASE 0
+#define STB0899_WIDTH_CRL_INIT_PHASE 24
+
+#define STB0899_OFF0_CRL_FREQ_INIT 0xf330
+#define STB0899_BASE_CRL_FREQ_INIT 0x00000000
+#define STB0899_CRL_FREQ_INIT_31 (0x1 << 30)
+#define STB0899_OFFST_CRL_FREQ_INIT_31 30
+#define STB0899_WIDTH_CRL_FREQ_INIT_31 1
+#define STB0899_CRL_LD_FREQ_INIT (0x1 << 24)
+#define STB0899_OFFST_CRL_LD_FREQ_INIT 24
+#define STB0899_WIDTH_CRL_LD_FREQ_INIT 1
+#define STB0899_CRL_FREQ_INIT (0xffffff << 0)
+#define STB0899_OFFST_CRL_FREQ_INIT 0
+#define STB0899_WIDTH_CRL_FREQ_INIT 24
+
+#define STB0899_OFF0_CRL_LOOP_GAIN 0xf334
+#define STB0899_BASE_CRL_LOOP_GAIN 0x00000000
+#define STB0899_KCRL2_RSHFT (0xf << 16)
+#define STB0899_OFFST_KCRL2_RSHFT 16
+#define STB0899_WIDTH_KCRL2_RSHFT 4
+#define STB0899_KCRL1 (0xf << 12)
+#define STB0899_OFFST_KCRL1 12
+#define STB0899_WIDTH_KCRL1 4
+#define STB0899_KCRL1_RSHFT (0xf << 8)
+#define STB0899_OFFST_KCRL1_RSHFT 8
+#define STB0899_WIDTH_KCRL1_RSHFT 4
+#define STB0899_KCRL0 (0xf << 4)
+#define STB0899_OFFST_KCRL0 4
+#define STB0899_WIDTH_KCRL0 4
+#define STB0899_KCRL0_RSHFT (0xf << 0)
+#define STB0899_OFFST_KCRL0_RSHFT 0
+#define STB0899_WIDTH_KCRL0_RSHFT 4
+
+#define STB0899_OFF0_CRL_NOM_FREQ 0xf338
+#define STB0899_BASE_CRL_NOM_FREQ 0x00000000
+#define STB0899_CRL_NOM_FREQ (0x3fffffff << 0)
+#define STB0899_OFFST_CRL_NOM_FREQ 0
+#define STB0899_WIDTH_CRL_NOM_FREQ 30
+
+#define STB0899_OFF0_CRL_SWP_RATE 0xf33c
+#define STB0899_BASE_CRL_SWP_RATE 0x00000000
+#define STB0899_CRL_SWP_RATE (0x3fffffff << 0)
+#define STB0899_OFFST_CRL_SWP_RATE 0
+#define STB0899_WIDTH_CRL_SWP_RATE 30
+
+#define STB0899_OFF0_CRL_MAX_SWP 0xf340
+#define STB0899_BASE_CRL_MAX_SWP 0x00000000
+#define STB0899_CRL_MAX_SWP (0x3fffffff << 0)
+#define STB0899_OFFST_CRL_MAX_SWP 0
+#define STB0899_WIDTH_CRL_MAX_SWP 30
+
+#define STB0899_OFF0_CRL_LK_CNTRL 0xf344
+#define STB0899_BASE_CRL_LK_CNTRL 0x00000000
+
+#define STB0899_OFF0_DECIM_CNTRL 0xf348
+#define STB0899_BASE_DECIM_CNTRL 0x00000000
+#define STB0899_BAND_LIMIT_B (0x01 << 5)
+#define STB0899_OFFST_BAND_LIMIT_B 5
+#define STB0899_WIDTH_BAND_LIMIT_B 1
+#define STB0899_WIN_SEL (0x03 << 3)
+#define STB0899_OFFST_WIN_SEL 3
+#define STB0899_WIDTH_WIN_SEL 2
+#define STB0899_DECIM_RATE (0x07 << 0)
+#define STB0899_OFFST_DECIM_RATE 0
+#define STB0899_WIDTH_DECIM_RATE 3
+
+#define STB0899_OFF0_BTR_CNTRL 0xf34c
+#define STB0899_BASE_BTR_CNTRL 0x00000000
+#define STB0899_BTR_FREQ_CORR (0x7ff << 4)
+#define STB0899_OFFST_BTR_FREQ_CORR 4
+#define STB0899_WIDTH_BTR_FREQ_CORR 11
+#define STB0899_BTR_CLR_LOCK (0x01 << 3)
+#define STB0899_OFFST_BTR_CLR_LOCK 3
+#define STB0899_WIDTH_BTR_CLR_LOCK 1
+#define STB0899_BTR_SENSE (0x01 << 2)
+#define STB0899_OFFST_BTR_SENSE 2
+#define STB0899_WIDTH_BTR_SENSE 1
+#define STB0899_BTR_ERR_ENA (0x01 << 1)
+#define STB0899_OFFST_BTR_ERR_ENA 1
+#define STB0899_WIDTH_BTR_ERR_ENA 1
+#define STB0899_INTRP_PHS_SENSE (0x01 << 0)
+#define STB0899_OFFST_INTRP_PHS_SENSE 0
+#define STB0899_WIDTH_INTRP_PHS_SENSE 1
+
+#define STB0899_OFF0_BTR_LOOP_GAIN 0xf350
+#define STB0899_BASE_BTR_LOOP_GAIN 0x00000000
+#define STB0899_KBTR2_RSHFT (0x0f << 16)
+#define STB0899_OFFST_KBTR2_RSHFT 16
+#define STB0899_WIDTH_KBTR2_RSHFT 4
+#define STB0899_KBTR1 (0x0f << 12)
+#define STB0899_OFFST_KBTR1 12
+#define STB0899_WIDTH_KBTR1 4
+#define STB0899_KBTR1_RSHFT (0x0f << 8)
+#define STB0899_OFFST_KBTR1_RSHFT 8
+#define STB0899_WIDTH_KBTR1_RSHFT 4
+#define STB0899_KBTR0 (0x0f << 4)
+#define STB0899_OFFST_KBTR0 4
+#define STB0899_WIDTH_KBTR0 4
+#define STB0899_KBTR0_RSHFT (0x0f << 0)
+#define STB0899_OFFST_KBTR0_RSHFT 0
+#define STB0899_WIDTH_KBTR0_RSHFT 4
+
+#define STB0899_OFF0_BTR_PHS_INIT 0xf354
+#define STB0899_BASE_BTR_PHS_INIT 0x00000000
+#define STB0899_BTR_LD_PHASE_INIT (0x01 << 28)
+#define STB0899_OFFST_BTR_LD_PHASE_INIT 28
+#define STB0899_WIDTH_BTR_LD_PHASE_INIT 1
+#define STB0899_BTR_INIT_PHASE (0xfffffff << 0)
+#define STB0899_OFFST_BTR_INIT_PHASE 0
+#define STB0899_WIDTH_BTR_INIT_PHASE 28
+
+#define STB0899_OFF0_BTR_FREQ_INIT 0xf358
+#define STB0899_BASE_BTR_FREQ_INIT 0x00000000
+#define STB0899_BTR_LD_FREQ_INIT (1 << 28)
+#define STB0899_OFFST_BTR_LD_FREQ_INIT 28
+#define STB0899_WIDTH_BTR_LD_FREQ_INIT 1
+#define STB0899_BTR_FREQ_INIT (0xfffffff << 0)
+#define STB0899_OFFST_BTR_FREQ_INIT 0
+#define STB0899_WIDTH_BTR_FREQ_INIT 28
+
+#define STB0899_OFF0_BTR_NOM_FREQ 0xf35c
+#define STB0899_BASE_BTR_NOM_FREQ 0x00000000
+#define STB0899_BTR_NOM_FREQ (0xfffffff << 0)
+#define STB0899_OFFST_BTR_NOM_FREQ 0
+#define STB0899_WIDTH_BTR_NOM_FREQ 28
+
+#define STB0899_OFF0_BTR_LK_CNTRL 0xf360
+#define STB0899_BASE_BTR_LK_CNTRL 0x00000000
+#define STB0899_BTR_MIN_ENERGY (0x0f << 24)
+#define STB0899_OFFST_BTR_MIN_ENERGY 24
+#define STB0899_WIDTH_BTR_MIN_ENERGY 4
+#define STB0899_BTR_LOCK_TH_LO (0xff << 16)
+#define STB0899_OFFST_BTR_LOCK_TH_LO 16
+#define STB0899_WIDTH_BTR_LOCK_TH_LO 8
+#define STB0899_BTR_LOCK_TH_HI (0xff << 8)
+#define STB0899_OFFST_BTR_LOCK_TH_HI 8
+#define STB0899_WIDTH_BTR_LOCK_TH_HI 8
+#define STB0899_BTR_LOCK_GAIN (0x03 << 6)
+#define STB0899_OFFST_BTR_LOCK_GAIN 6
+#define STB0899_WIDTH_BTR_LOCK_GAIN 2
+#define STB0899_BTR_LOCK_LEAK (0x3f << 0)
+#define STB0899_OFFST_BTR_LOCK_LEAK 0
+#define STB0899_WIDTH_BTR_LOCK_LEAK 6
+
+#define STB0899_OFF0_DECN_CNTRL 0xf364
+#define STB0899_BASE_DECN_CNTRL 0x00000000
+
+#define STB0899_OFF0_TP_CNTRL 0xf368
+#define STB0899_BASE_TP_CNTRL 0x00000000
+
+#define STB0899_OFF0_TP_BUF_STATUS 0xf36c
+#define STB0899_BASE_TP_BUF_STATUS 0x00000000
+#define STB0899_TP_BUFFER_FULL (1 << 0)
+
+#define STB0899_OFF0_DC_ESTIM 0xf37c
+#define STB0899_BASE_DC_ESTIM 0x0000
+#define STB0899_I_DC_ESTIMATE (0xff << 8)
+#define STB0899_OFFST_I_DC_ESTIMATE 8
+#define STB0899_WIDTH_I_DC_ESTIMATE 8
+#define STB0899_Q_DC_ESTIMATE (0xff << 0)
+#define STB0899_OFFST_Q_DC_ESTIMATE 0
+#define STB0899_WIDTH_Q_DC_ESTIMATE 8
+
+#define STB0899_OFF0_FLL_CNTRL 0xf310
+#define STB0899_BASE_FLL_CNTRL 0x00000020
+#define STB0899_CRL_FLL_ACC (0x01 << 4)
+#define STB0899_OFFST_CRL_FLL_ACC 4
+#define STB0899_WIDTH_CRL_FLL_ACC 1
+#define STB0899_FLL_AVG_PERIOD (0x0f << 0)
+#define STB0899_OFFST_FLL_AVG_PERIOD 0
+#define STB0899_WIDTH_FLL_AVG_PERIOD 4
+
+#define STB0899_OFF0_FLL_FREQ_WD 0xf314
+#define STB0899_BASE_FLL_FREQ_WD 0x00000020
+#define STB0899_FLL_FREQ_WD (0xffffffff << 0)
+#define STB0899_OFFST_FLL_FREQ_WD 0
+#define STB0899_WIDTH_FLL_FREQ_WD 32
+
+#define STB0899_OFF0_ANTI_ALIAS_SEL 0xf358
+#define STB0899_BASE_ANTI_ALIAS_SEL 0x00000020
+#define STB0899_ANTI_ALIAS_SELB (0x03 << 0)
+#define STB0899_OFFST_ANTI_ALIAS_SELB 0
+#define STB0899_WIDTH_ANTI_ALIAS_SELB 2
+
+#define STB0899_OFF0_RRC_ALPHA 0xf35c
+#define STB0899_BASE_RRC_ALPHA 0x00000020
+#define STB0899_RRC_ALPHA (0x03 << 0)
+#define STB0899_OFFST_RRC_ALPHA 0
+#define STB0899_WIDTH_RRC_ALPHA 2
+
+#define STB0899_OFF0_DC_ADAPT_LSHFT 0xf360
+#define STB0899_BASE_DC_ADAPT_LSHFT 0x00000020
+#define STB0899_DC_ADAPT_LSHFT (0x077 << 0)
+#define STB0899_OFFST_DC_ADAPT_LSHFT 0
+#define STB0899_WIDTH_DC_ADAPT_LSHFT 3
+
+#define STB0899_OFF0_IMB_OFFSET 0xf364
+#define STB0899_BASE_IMB_OFFSET 0x00000020
+#define STB0899_PHS_IMB_COMP (0xff << 8)
+#define STB0899_OFFST_PHS_IMB_COMP 8
+#define STB0899_WIDTH_PHS_IMB_COMP 8
+#define STB0899_AMPL_IMB_COMP (0xff << 0)
+#define STB0899_OFFST_AMPL_IMB_COMP 0
+#define STB0899_WIDTH_AMPL_IMB_COMP 8
+
+#define STB0899_OFF0_IMB_ESTIMATE 0xf368
+#define STB0899_BASE_IMB_ESTIMATE 0x00000020
+#define STB0899_PHS_IMB_ESTIMATE (0xff << 8)
+#define STB0899_OFFST_PHS_IMB_ESTIMATE 8
+#define STB0899_WIDTH_PHS_IMB_ESTIMATE 8
+#define STB0899_AMPL_IMB_ESTIMATE (0xff << 0)
+#define STB0899_OFFST_AMPL_IMB_ESTIMATE 0
+#define STB0899_WIDTH_AMPL_IMB_ESTIMATE 8
+
+#define STB0899_OFF0_IMB_CNTRL 0xf36c
+#define STB0899_BASE_IMB_CNTRL 0x00000020
+#define STB0899_PHS_ADAPT_LSHFT (0x07 << 4)
+#define STB0899_OFFST_PHS_ADAPT_LSHFT 4
+#define STB0899_WIDTH_PHS_ADAPT_LSHFT 3
+#define STB0899_AMPL_ADAPT_LSHFT (0x07 << 1)
+#define STB0899_OFFST_AMPL_ADAPT_LSHFT 1
+#define STB0899_WIDTH_AMPL_ADAPT_LSHFT 3
+#define STB0899_IMB_COMP (0x01 << 0)
+#define STB0899_OFFST_IMB_COMP 0
+#define STB0899_WIDTH_IMB_COMP 1
+
+#define STB0899_OFF0_IF_AGC_CNTRL2 0xf374
+#define STB0899_BASE_IF_AGC_CNTRL2 0x00000020
+#define STB0899_IF_AGC_LOCK_TH (0xff << 11)
+#define STB0899_OFFST_IF_AGC_LOCK_TH 11
+#define STB0899_WIDTH_IF_AGC_LOCK_TH 8
+#define STB0899_IF_AGC_SD_DIV (0xff << 3)
+#define STB0899_OFFST_IF_AGC_SD_DIV 3
+#define STB0899_WIDTH_IF_AGC_SD_DIV 8
+#define STB0899_IF_AGC_DUMP_PER (0x07 << 0)
+#define STB0899_OFFST_IF_AGC_DUMP_PER 0
+#define STB0899_WIDTH_IF_AGC_DUMP_PER 3
+
+#define STB0899_OFF0_DMD_CNTRL2 0xf378
+#define STB0899_BASE_DMD_CNTRL2 0x00000020
+#define STB0899_SPECTRUM_INVERT (0x01 << 2)
+#define STB0899_OFFST_SPECTRUM_INVERT 2
+#define STB0899_WIDTH_SPECTRUM_INVERT 1
+#define STB0899_AGC_MODE (0x01 << 1)
+#define STB0899_OFFST_AGC_MODE 1
+#define STB0899_WIDTH_AGC_MODE 1
+#define STB0899_CRL_FREQ_ADJ (0x01 << 0)
+#define STB0899_OFFST_CRL_FREQ_ADJ 0
+#define STB0899_WIDTH_CRL_FREQ_ADJ 1
+
+#define STB0899_OFF0_TP_BUFFER 0xf300
+#define STB0899_BASE_TP_BUFFER 0x00000040
+#define STB0899_TP_BUFFER_IN (0xffff << 0)
+#define STB0899_OFFST_TP_BUFFER_IN 0
+#define STB0899_WIDTH_TP_BUFFER_IN 16
+
+#define STB0899_OFF0_TP_BUFFER1 0xf304
+#define STB0899_BASE_TP_BUFFER1 0x00000040
+#define STB0899_OFF0_TP_BUFFER2 0xf308
+#define STB0899_BASE_TP_BUFFER2 0x00000040
+#define STB0899_OFF0_TP_BUFFER3 0xf30c
+#define STB0899_BASE_TP_BUFFER3 0x00000040
+#define STB0899_OFF0_TP_BUFFER4 0xf310
+#define STB0899_BASE_TP_BUFFER4 0x00000040
+#define STB0899_OFF0_TP_BUFFER5 0xf314
+#define STB0899_BASE_TP_BUFFER5 0x00000040
+#define STB0899_OFF0_TP_BUFFER6 0xf318
+#define STB0899_BASE_TP_BUFFER6 0x00000040
+#define STB0899_OFF0_TP_BUFFER7 0xf31c
+#define STB0899_BASE_TP_BUFFER7 0x00000040
+#define STB0899_OFF0_TP_BUFFER8 0xf320
+#define STB0899_BASE_TP_BUFFER8 0x00000040
+#define STB0899_OFF0_TP_BUFFER9 0xf324
+#define STB0899_BASE_TP_BUFFER9 0x00000040
+#define STB0899_OFF0_TP_BUFFER10 0xf328
+#define STB0899_BASE_TP_BUFFER10 0x00000040
+#define STB0899_OFF0_TP_BUFFER11 0xf32c
+#define STB0899_BASE_TP_BUFFER11 0x00000040
+#define STB0899_OFF0_TP_BUFFER12 0xf330
+#define STB0899_BASE_TP_BUFFER12 0x00000040
+#define STB0899_OFF0_TP_BUFFER13 0xf334
+#define STB0899_BASE_TP_BUFFER13 0x00000040
+#define STB0899_OFF0_TP_BUFFER14 0xf338
+#define STB0899_BASE_TP_BUFFER14 0x00000040
+#define STB0899_OFF0_TP_BUFFER15 0xf33c
+#define STB0899_BASE_TP_BUFFER15 0x00000040
+#define STB0899_OFF0_TP_BUFFER16 0xf340
+#define STB0899_BASE_TP_BUFFER16 0x00000040
+#define STB0899_OFF0_TP_BUFFER17 0xf344
+#define STB0899_BASE_TP_BUFFER17 0x00000040
+#define STB0899_OFF0_TP_BUFFER18 0xf348
+#define STB0899_BASE_TP_BUFFER18 0x00000040
+#define STB0899_OFF0_TP_BUFFER19 0xf34c
+#define STB0899_BASE_TP_BUFFER19 0x00000040
+#define STB0899_OFF0_TP_BUFFER20 0xf350
+#define STB0899_BASE_TP_BUFFER20 0x00000040
+#define STB0899_OFF0_TP_BUFFER21 0xf354
+#define STB0899_BASE_TP_BUFFER21 0x00000040
+#define STB0899_OFF0_TP_BUFFER22 0xf358
+#define STB0899_BASE_TP_BUFFER22 0x00000040
+#define STB0899_OFF0_TP_BUFFER23 0xf35c
+#define STB0899_BASE_TP_BUFFER23 0x00000040
+#define STB0899_OFF0_TP_BUFFER24 0xf360
+#define STB0899_BASE_TP_BUFFER24 0x00000040
+#define STB0899_OFF0_TP_BUFFER25 0xf364
+#define STB0899_BASE_TP_BUFFER25 0x00000040
+#define STB0899_OFF0_TP_BUFFER26 0xf368
+#define STB0899_BASE_TP_BUFFER26 0x00000040
+#define STB0899_OFF0_TP_BUFFER27 0xf36c
+#define STB0899_BASE_TP_BUFFER27 0x00000040
+#define STB0899_OFF0_TP_BUFFER28 0xf370
+#define STB0899_BASE_TP_BUFFER28 0x00000040
+#define STB0899_OFF0_TP_BUFFER29 0xf374
+#define STB0899_BASE_TP_BUFFER29 0x00000040
+#define STB0899_OFF0_TP_BUFFER30 0xf378
+#define STB0899_BASE_TP_BUFFER30 0x00000040
+#define STB0899_OFF0_TP_BUFFER31 0xf37c
+#define STB0899_BASE_TP_BUFFER31 0x00000040
+#define STB0899_OFF0_TP_BUFFER32 0xf300
+#define STB0899_BASE_TP_BUFFER32 0x00000060
+#define STB0899_OFF0_TP_BUFFER33 0xf304
+#define STB0899_BASE_TP_BUFFER33 0x00000060
+#define STB0899_OFF0_TP_BUFFER34 0xf308
+#define STB0899_BASE_TP_BUFFER34 0x00000060
+#define STB0899_OFF0_TP_BUFFER35 0xf30c
+#define STB0899_BASE_TP_BUFFER35 0x00000060
+#define STB0899_OFF0_TP_BUFFER36 0xf310
+#define STB0899_BASE_TP_BUFFER36 0x00000060
+#define STB0899_OFF0_TP_BUFFER37 0xf314
+#define STB0899_BASE_TP_BUFFER37 0x00000060
+#define STB0899_OFF0_TP_BUFFER38 0xf318
+#define STB0899_BASE_TP_BUFFER38 0x00000060
+#define STB0899_OFF0_TP_BUFFER39 0xf31c
+#define STB0899_BASE_TP_BUFFER39 0x00000060
+#define STB0899_OFF0_TP_BUFFER40 0xf320
+#define STB0899_BASE_TP_BUFFER40 0x00000060
+#define STB0899_OFF0_TP_BUFFER41 0xf324
+#define STB0899_BASE_TP_BUFFER41 0x00000060
+#define STB0899_OFF0_TP_BUFFER42 0xf328
+#define STB0899_BASE_TP_BUFFER42 0x00000060
+#define STB0899_OFF0_TP_BUFFER43 0xf32c
+#define STB0899_BASE_TP_BUFFER43 0x00000060
+#define STB0899_OFF0_TP_BUFFER44 0xf330
+#define STB0899_BASE_TP_BUFFER44 0x00000060
+#define STB0899_OFF0_TP_BUFFER45 0xf334
+#define STB0899_BASE_TP_BUFFER45 0x00000060
+#define STB0899_OFF0_TP_BUFFER46 0xf338
+#define STB0899_BASE_TP_BUFFER46 0x00000060
+#define STB0899_OFF0_TP_BUFFER47 0xf33c
+#define STB0899_BASE_TP_BUFFER47 0x00000060
+#define STB0899_OFF0_TP_BUFFER48 0xf340
+#define STB0899_BASE_TP_BUFFER48 0x00000060
+#define STB0899_OFF0_TP_BUFFER49 0xf344
+#define STB0899_BASE_TP_BUFFER49 0x00000060
+#define STB0899_OFF0_TP_BUFFER50 0xf348
+#define STB0899_BASE_TP_BUFFER50 0x00000060
+#define STB0899_OFF0_TP_BUFFER51 0xf34c
+#define STB0899_BASE_TP_BUFFER51 0x00000060
+#define STB0899_OFF0_TP_BUFFER52 0xf350
+#define STB0899_BASE_TP_BUFFER52 0x00000060
+#define STB0899_OFF0_TP_BUFFER53 0xf354
+#define STB0899_BASE_TP_BUFFER53 0x00000060
+#define STB0899_OFF0_TP_BUFFER54 0xf358
+#define STB0899_BASE_TP_BUFFER54 0x00000060
+#define STB0899_OFF0_TP_BUFFER55 0xf35c
+#define STB0899_BASE_TP_BUFFER55 0x00000060
+#define STB0899_OFF0_TP_BUFFER56 0xf360
+#define STB0899_BASE_TP_BUFFER56 0x00000060
+#define STB0899_OFF0_TP_BUFFER57 0xf364
+#define STB0899_BASE_TP_BUFFER57 0x00000060
+#define STB0899_OFF0_TP_BUFFER58 0xf368
+#define STB0899_BASE_TP_BUFFER58 0x00000060
+#define STB0899_OFF0_TP_BUFFER59 0xf36c
+#define STB0899_BASE_TP_BUFFER59 0x00000060
+#define STB0899_OFF0_TP_BUFFER60 0xf370
+#define STB0899_BASE_TP_BUFFER60 0x00000060
+#define STB0899_OFF0_TP_BUFFER61 0xf374
+#define STB0899_BASE_TP_BUFFER61 0x00000060
+#define STB0899_OFF0_TP_BUFFER62 0xf378
+#define STB0899_BASE_TP_BUFFER62 0x00000060
+#define STB0899_OFF0_TP_BUFFER63 0xf37c
+#define STB0899_BASE_TP_BUFFER63 0x00000060
+
+#define STB0899_OFF0_RESET_CNTRL 0xf300
+#define STB0899_BASE_RESET_CNTRL 0x00000400
+#define STB0899_DVBS2_RESET (0x01 << 0)
+#define STB0899_OFFST_DVBS2_RESET 0
+#define STB0899_WIDTH_DVBS2_RESET 1
+
+#define STB0899_OFF0_ACM_ENABLE 0xf304
+#define STB0899_BASE_ACM_ENABLE 0x00000400
+#define STB0899_ACM_ENABLE 1
+
+#define STB0899_OFF0_DESCR_CNTRL 0xf30c
+#define STB0899_BASE_DESCR_CNTRL 0x00000400
+#define STB0899_OFFST_DESCR_CNTRL 0
+#define STB0899_WIDTH_DESCR_CNTRL 16
+
+#define STB0899_OFF0_UWP_CNTRL1 0xf320
+#define STB0899_BASE_UWP_CNTRL1 0x00000400
+#define STB0899_UWP_TH_SOF (0x7fff << 11)
+#define STB0899_OFFST_UWP_TH_SOF 11
+#define STB0899_WIDTH_UWP_TH_SOF 15
+#define STB0899_UWP_ESN0_QUANT (0xff << 3)
+#define STB0899_OFFST_UWP_ESN0_QUANT 3
+#define STB0899_WIDTH_UWP_ESN0_QUANT 8
+#define STB0899_UWP_ESN0_AVE (0x03 << 1)
+#define STB0899_OFFST_UWP_ESN0_AVE 1
+#define STB0899_WIDTH_UWP_ESN0_AVE 2
+#define STB0899_UWP_START (0x01 << 0)
+#define STB0899_OFFST_UWP_START 0
+#define STB0899_WIDTH_UWP_START 1
+
+#define STB0899_OFF0_UWP_CNTRL2 0xf324
+#define STB0899_BASE_UWP_CNTRL2 0x00000400
+#define STB0899_UWP_MISS_TH (0xff << 16)
+#define STB0899_OFFST_UWP_MISS_TH 16
+#define STB0899_WIDTH_UWP_MISS_TH 8
+#define STB0899_FE_FINE_TRK (0xff << 8)
+#define STB0899_OFFST_FE_FINE_TRK 8
+#define STB0899_WIDTH_FE_FINE_TRK 8
+#define STB0899_FE_COARSE_TRK (0xff << 0)
+#define STB0899_OFFST_FE_COARSE_TRK 0
+#define STB0899_WIDTH_FE_COARSE_TRK 8
+
+#define STB0899_OFF0_UWP_STAT1 0xf328
+#define STB0899_BASE_UWP_STAT1 0x00000400
+#define STB0899_UWP_STATE (0x03ff << 15)
+#define STB0899_OFFST_UWP_STATE 15
+#define STB0899_WIDTH_UWP_STATE 10
+#define STB0899_UW_MAX_PEAK (0x7fff << 0)
+#define STB0899_OFFST_UW_MAX_PEAK 0
+#define STB0899_WIDTH_UW_MAX_PEAK 15
+
+#define STB0899_OFF0_UWP_STAT2 0xf32c
+#define STB0899_BASE_UWP_STAT2 0x00000400
+#define STB0899_ESNO_EST (0x07ffff << 7)
+#define STB0899_OFFST_ESN0_EST 7
+#define STB0899_WIDTH_ESN0_EST 19
+#define STB0899_UWP_DECODE_MOD (0x7f << 0)
+#define STB0899_OFFST_UWP_DECODE_MOD 0
+#define STB0899_WIDTH_UWP_DECODE_MOD 7
+
+#define STB0899_OFF0_DMD_CORE_ID 0xf334
+#define STB0899_BASE_DMD_CORE_ID 0x00000400
+#define STB0899_CORE_ID (0xffffffff << 0)
+#define STB0899_OFFST_CORE_ID 0
+#define STB0899_WIDTH_CORE_ID 32
+
+#define STB0899_OFF0_DMD_VERSION_ID 0xf33c
+#define STB0899_BASE_DMD_VERSION_ID 0x00000400
+#define STB0899_VERSION_ID (0xff << 0)
+#define STB0899_OFFST_VERSION_ID 0
+#define STB0899_WIDTH_VERSION_ID 8
+
+#define STB0899_OFF0_DMD_STAT2 0xf340
+#define STB0899_BASE_DMD_STAT2 0x00000400
+#define STB0899_CSM_LOCK (0x01 << 1)
+#define STB0899_OFFST_CSM_LOCK 1
+#define STB0899_WIDTH_CSM_LOCK 1
+#define STB0899_UWP_LOCK (0x01 << 0)
+#define STB0899_OFFST_UWP_LOCK 0
+#define STB0899_WIDTH_UWP_LOCK 1
+
+#define STB0899_OFF0_FREQ_ADJ_SCALE 0xf344
+#define STB0899_BASE_FREQ_ADJ_SCALE 0x00000400
+#define STB0899_FREQ_ADJ_SCALE (0x0fff << 0)
+#define STB0899_OFFST_FREQ_ADJ_SCALE 0
+#define STB0899_WIDTH_FREQ_ADJ_SCALE 12
+
+#define STB0899_OFF0_UWP_CNTRL3 0xf34c
+#define STB0899_BASE_UWP_CNTRL3 0x00000400
+#define STB0899_UWP_TH_TRACK (0x7fff << 15)
+#define STB0899_OFFST_UWP_TH_TRACK 15
+#define STB0899_WIDTH_UWP_TH_TRACK 15
+#define STB0899_UWP_TH_ACQ (0x7fff << 0)
+#define STB0899_OFFST_UWP_TH_ACQ 0
+#define STB0899_WIDTH_UWP_TH_ACQ 15
+
+#define STB0899_OFF0_SYM_CLK_SEL 0xf350
+#define STB0899_BASE_SYM_CLK_SEL 0x00000400
+#define STB0899_SYM_CLK_SEL (0x03 << 0)
+#define STB0899_OFFST_SYM_CLK_SEL 0
+#define STB0899_WIDTH_SYM_CLK_SEL 2
+
+#define STB0899_OFF0_SOF_SRCH_TO 0xf354
+#define STB0899_BASE_SOF_SRCH_TO 0x00000400
+#define STB0899_SOF_SEARCH_TIMEOUT (0x3fffff << 0)
+#define STB0899_OFFST_SOF_SEARCH_TIMEOUT 0
+#define STB0899_WIDTH_SOF_SEARCH_TIMEOUT 22
+
+#define STB0899_OFF0_ACQ_CNTRL1 0xf358
+#define STB0899_BASE_ACQ_CNTRL1 0x00000400
+#define STB0899_FE_FINE_ACQ (0xff << 8)
+#define STB0899_OFFST_FE_FINE_ACQ 8
+#define STB0899_WIDTH_FE_FINE_ACQ 8
+#define STB0899_FE_COARSE_ACQ (0xff << 0)
+#define STB0899_OFFST_FE_COARSE_ACQ 0
+#define STB0899_WIDTH_FE_COARSE_ACQ 8
+
+#define STB0899_OFF0_ACQ_CNTRL2 0xf35c
+#define STB0899_BASE_ACQ_CNTRL2 0x00000400
+#define STB0899_ZIGZAG (0x01 << 25)
+#define STB0899_OFFST_ZIGZAG 25
+#define STB0899_WIDTH_ZIGZAG 1
+#define STB0899_NUM_STEPS (0xff << 17)
+#define STB0899_OFFST_NUM_STEPS 17
+#define STB0899_WIDTH_NUM_STEPS 8
+#define STB0899_FREQ_STEPSIZE (0x1ffff << 0)
+#define STB0899_OFFST_FREQ_STEPSIZE 0
+#define STB0899_WIDTH_FREQ_STEPSIZE 17
+
+#define STB0899_OFF0_ACQ_CNTRL3 0xf360
+#define STB0899_BASE_ACQ_CNTRL3 0x00000400
+#define STB0899_THRESHOLD_SCL (0x3f << 23)
+#define STB0899_OFFST_THRESHOLD_SCL 23
+#define STB0899_WIDTH_THRESHOLD_SCL 6
+#define STB0899_UWP_TH_SRCH (0x7fff << 8)
+#define STB0899_OFFST_UWP_TH_SRCH 8
+#define STB0899_WIDTH_UWP_TH_SRCH 15
+#define STB0899_AUTO_REACQUIRE (0x01 << 7)
+#define STB0899_OFFST_AUTO_REACQUIRE 7
+#define STB0899_WIDTH_AUTO_REACQUIRE 1
+#define STB0899_TRACK_LOCK_SEL (0x01 << 6)
+#define STB0899_OFFST_TRACK_LOCK_SEL 6
+#define STB0899_WIDTH_TRACK_LOCK_SEL 1
+#define STB0899_ACQ_SEARCH_MODE (0x03 << 4)
+#define STB0899_OFFST_ACQ_SEARCH_MODE 4
+#define STB0899_WIDTH_ACQ_SEARCH_MODE 2
+#define STB0899_CONFIRM_FRAMES (0x0f << 0)
+#define STB0899_OFFST_CONFIRM_FRAMES 0
+#define STB0899_WIDTH_CONFIRM_FRAMES 4
+
+#define STB0899_OFF0_FE_SETTLE 0xf364
+#define STB0899_BASE_FE_SETTLE 0x00000400
+#define STB0899_SETTLING_TIME (0x3fffff << 0)
+#define STB0899_OFFST_SETTLING_TIME 0
+#define STB0899_WIDTH_SETTLING_TIME 22
+
+#define STB0899_OFF0_AC_DWELL 0xf368
+#define STB0899_BASE_AC_DWELL 0x00000400
+#define STB0899_DWELL_TIME (0x3fffff << 0)
+#define STB0899_OFFST_DWELL_TIME 0
+#define STB0899_WIDTH_DWELL_TIME 22
+
+#define STB0899_OFF0_ACQUIRE_TRIG 0xf36c
+#define STB0899_BASE_ACQUIRE_TRIG 0x00000400
+#define STB0899_ACQUIRE (0x01 << 0)
+#define STB0899_OFFST_ACQUIRE 0
+#define STB0899_WIDTH_ACQUIRE 1
+
+#define STB0899_OFF0_LOCK_LOST 0xf370
+#define STB0899_BASE_LOCK_LOST 0x00000400
+#define STB0899_LOCK_LOST (0x01 << 0)
+#define STB0899_OFFST_LOCK_LOST 0
+#define STB0899_WIDTH_LOCK_LOST 1
+
+#define STB0899_OFF0_ACQ_STAT1 0xf374
+#define STB0899_BASE_ACQ_STAT1 0x00000400
+#define STB0899_STEP_FREQ (0x1fffff << 11)
+#define STB0899_OFFST_STEP_FREQ 11
+#define STB0899_WIDTH_STEP_FREQ 21
+#define STB0899_ACQ_STATE (0x07 << 8)
+#define STB0899_OFFST_ACQ_STATE 8
+#define STB0899_WIDTH_ACQ_STATE 3
+#define STB0899_UW_DETECT_COUNT (0xff << 0)
+#define STB0899_OFFST_UW_DETECT_COUNT 0
+#define STB0899_WIDTH_UW_DETECT_COUNT 8
+
+#define STB0899_OFF0_ACQ_TIMEOUT 0xf378
+#define STB0899_BASE_ACQ_TIMEOUT 0x00000400
+#define STB0899_ACQ_TIMEOUT (0x3fffff << 0)
+#define STB0899_OFFST_ACQ_TIMEOUT 0
+#define STB0899_WIDTH_ACQ_TIMEOUT 22
+
+#define STB0899_OFF0_ACQ_TIME 0xf37c
+#define STB0899_BASE_ACQ_TIME 0x00000400
+#define STB0899_ACQ_TIME_SYM (0xffffff << 0)
+#define STB0899_OFFST_ACQ_TIME_SYM 0
+#define STB0899_WIDTH_ACQ_TIME_SYM 24
+
+#define STB0899_OFF0_FINAL_AGC_CNTRL 0xf308
+#define STB0899_BASE_FINAL_AGC_CNTRL 0x00000440
+#define STB0899_FINAL_GAIN_INIT (0x3fff << 12)
+#define STB0899_OFFST_FINAL_GAIN_INIT 12
+#define STB0899_WIDTH_FINAL_GAIN_INIT 14
+#define STB0899_FINAL_LOOP_GAIN (0x0f << 8)
+#define STB0899_OFFST_FINAL_LOOP_GAIN 8
+#define STB0899_WIDTH_FINAL_LOOP_GAIN 4
+#define STB0899_FINAL_LD_GAIN_INIT (0x01 << 7)
+#define STB0899_OFFST_FINAL_LD_GAIN_INIT 7
+#define STB0899_WIDTH_FINAL_LD_GAIN_INIT 1
+#define STB0899_FINAL_AGC_REF (0x7f << 0)
+#define STB0899_OFFST_FINAL_AGC_REF 0
+#define STB0899_WIDTH_FINAL_AGC_REF 7
+
+#define STB0899_OFF0_FINAL_AGC_GAIN 0xf30c
+#define STB0899_BASE_FINAL_AGC_GAIN 0x00000440
+#define STB0899_FINAL_AGC_GAIN (0x3fff << 0)
+#define STB0899_OFFST_FINAL_AGC_GAIN 0
+#define STB0899_WIDTH_FINAL_AGC_GAIN 14
+
+#define STB0899_OFF0_EQUALIZER_INIT 0xf310
+#define STB0899_BASE_EQUALIZER_INIT 0x00000440
+#define STB0899_EQ_SRST (0x01 << 1)
+#define STB0899_OFFST_EQ_SRST 1
+#define STB0899_WIDTH_EQ_SRST 1
+#define STB0899_EQ_INIT (0x01 << 0)
+#define STB0899_OFFST_EQ_INIT 0
+#define STB0899_WIDTH_EQ_INIT 1
+
+#define STB0899_OFF0_EQ_CNTRL 0xf314
+#define STB0899_BASE_EQ_CNTRL 0x00000440
+#define STB0899_EQ_ADAPT_MODE (0x01 << 18)
+#define STB0899_OFFST_EQ_ADAPT_MODE 18
+#define STB0899_WIDTH_EQ_ADAPT_MODE 1
+#define STB0899_EQ_DELAY (0x0f << 14)
+#define STB0899_OFFST_EQ_DELAY 14
+#define STB0899_WIDTH_EQ_DELAY 4
+#define STB0899_EQ_QUANT_LEVEL (0xff << 6)
+#define STB0899_OFFST_EQ_QUANT_LEVEL 6
+#define STB0899_WIDTH_EQ_QUANT_LEVEL 8
+#define STB0899_EQ_DISABLE_UPDATE (0x01 << 5)
+#define STB0899_OFFST_EQ_DISABLE_UPDATE 5
+#define STB0899_WIDTH_EQ_DISABLE_UPDATE 1
+#define STB0899_EQ_BYPASS (0x01 << 4)
+#define STB0899_OFFST_EQ_BYPASS 4
+#define STB0899_WIDTH_EQ_BYPASS 1
+#define STB0899_EQ_SHIFT (0x0f << 0)
+#define STB0899_OFFST_EQ_SHIFT 0
+#define STB0899_WIDTH_EQ_SHIFT 4
+
+#define STB0899_OFF0_EQ_I_INIT_COEFF_0 0xf320
+#define STB0899_OFF1_EQ_I_INIT_COEFF_1 0xf324
+#define STB0899_OFF2_EQ_I_INIT_COEFF_2 0xf328
+#define STB0899_OFF3_EQ_I_INIT_COEFF_3 0xf32c
+#define STB0899_OFF4_EQ_I_INIT_COEFF_4 0xf330
+#define STB0899_OFF5_EQ_I_INIT_COEFF_5 0xf334
+#define STB0899_OFF6_EQ_I_INIT_COEFF_6 0xf338
+#define STB0899_OFF7_EQ_I_INIT_COEFF_7 0xf33c
+#define STB0899_OFF8_EQ_I_INIT_COEFF_8 0xf340
+#define STB0899_OFF9_EQ_I_INIT_COEFF_9 0xf344
+#define STB0899_OFFa_EQ_I_INIT_COEFF_10 0xf348
+#define STB0899_BASE_EQ_I_INIT_COEFF_N 0x00000440
+#define STB0899_EQ_I_INIT_COEFF_N (0x0fff << 0)
+#define STB0899_OFFST_EQ_I_INIT_COEFF_N 0
+#define STB0899_WIDTH_EQ_I_INIT_COEFF_N 12
+
+#define STB0899_OFF0_EQ_Q_INIT_COEFF_0 0xf350
+#define STB0899_OFF1_EQ_Q_INIT_COEFF_1 0xf354
+#define STB0899_OFF2_EQ_Q_INIT_COEFF_2 0xf358
+#define STB0899_OFF3_EQ_Q_INIT_COEFF_3 0xf35c
+#define STB0899_OFF4_EQ_Q_INIT_COEFF_4 0xf360
+#define STB0899_OFF5_EQ_Q_INIT_COEFF_5 0xf364
+#define STB0899_OFF6_EQ_Q_INIT_COEFF_6 0xf368
+#define STB0899_OFF7_EQ_Q_INIT_COEFF_7 0xf36c
+#define STB0899_OFF8_EQ_Q_INIT_COEFF_8 0xf370
+#define STB0899_OFF9_EQ_Q_INIT_COEFF_9 0xf374
+#define STB0899_OFFa_EQ_Q_INIT_COEFF_10 0xf378
+#define STB0899_BASE_EQ_Q_INIT_COEFF_N 0x00000440
+#define STB0899_EQ_Q_INIT_COEFF_N (0x0fff << 0)
+#define STB0899_OFFST_EQ_Q_INIT_COEFF_N 0
+#define STB0899_WIDTH_EQ_Q_INIT_COEFF_N 12
+
+#define STB0899_OFF0_EQ_I_OUT_COEFF_0 0xf300
+#define STB0899_OFF1_EQ_I_OUT_COEFF_1 0xf304
+#define STB0899_OFF2_EQ_I_OUT_COEFF_2 0xf308
+#define STB0899_OFF3_EQ_I_OUT_COEFF_3 0xf30c
+#define STB0899_OFF4_EQ_I_OUT_COEFF_4 0xf310
+#define STB0899_OFF5_EQ_I_OUT_COEFF_5 0xf314
+#define STB0899_OFF6_EQ_I_OUT_COEFF_6 0xf318
+#define STB0899_OFF7_EQ_I_OUT_COEFF_7 0xf31c
+#define STB0899_OFF8_EQ_I_OUT_COEFF_8 0xf320
+#define STB0899_OFF9_EQ_I_OUT_COEFF_9 0xf324
+#define STB0899_OFFa_EQ_I_OUT_COEFF_10 0xf328
+#define STB0899_BASE_EQ_I_OUT_COEFF_N 0x00000460
+#define STB0899_EQ_I_OUT_COEFF_N (0x0fff << 0)
+#define STB0899_OFFST_EQ_I_OUT_COEFF_N 0
+#define STB0899_WIDTH_EQ_I_OUT_COEFF_N 12
+
+#define STB0899_OFF0_EQ_Q_OUT_COEFF_0 0xf330
+#define STB0899_OFF1_EQ_Q_OUT_COEFF_1 0xf334
+#define STB0899_OFF2_EQ_Q_OUT_COEFF_2 0xf338
+#define STB0899_OFF3_EQ_Q_OUT_COEFF_3 0xf33c
+#define STB0899_OFF4_EQ_Q_OUT_COEFF_4 0xf340
+#define STB0899_OFF5_EQ_Q_OUT_COEFF_5 0xf344
+#define STB0899_OFF6_EQ_Q_OUT_COEFF_6 0xf348
+#define STB0899_OFF7_EQ_Q_OUT_COEFF_7 0xf34c
+#define STB0899_OFF8_EQ_Q_OUT_COEFF_8 0xf350
+#define STB0899_OFF9_EQ_Q_OUT_COEFF_9 0xf354
+#define STB0899_OFFa_EQ_Q_OUT_COEFF_10 0xf358
+#define STB0899_BASE_EQ_Q_OUT_COEFF_N 0x00000460
+#define STB0899_EQ_Q_OUT_COEFF_N (0x0fff << 0)
+#define STB0899_OFFST_EQ_Q_OUT_COEFF_N 0
+#define STB0899_WIDTH_EQ_Q_OUT_COEFF_N 12
+
+/* S2 FEC */
+#define STB0899_OFF0_BLOCK_LNGTH 0xfa04
+#define STB0899_BASE_BLOCK_LNGTH 0x00000000
+#define STB0899_BLOCK_LENGTH (0xff << 0)
+#define STB0899_OFFST_BLOCK_LENGTH 0
+#define STB0899_WIDTH_BLOCK_LENGTH 8
+
+#define STB0899_OFF0_ROW_STR 0xfa08
+#define STB0899_BASE_ROW_STR 0x00000000
+#define STB0899_ROW_STRIDE (0xff << 0)
+#define STB0899_OFFST_ROW_STRIDE 0
+#define STB0899_WIDTH_ROW_STRIDE 8
+
+#define STB0899_OFF0_MAX_ITER 0xfa0c
+#define STB0899_BASE_MAX_ITER 0x00000000
+#define STB0899_MAX_ITERATIONS (0xff << 0)
+#define STB0899_OFFST_MAX_ITERATIONS 0
+#define STB0899_WIDTH_MAX_ITERATIONS 8
+
+#define STB0899_OFF0_BN_END_ADDR 0xfa10
+#define STB0899_BASE_BN_END_ADDR 0x00000000
+#define STB0899_BN_END_ADDR (0x0fff << 0)
+#define STB0899_OFFST_BN_END_ADDR 0
+#define STB0899_WIDTH_BN_END_ADDR 12
+
+#define STB0899_OFF0_CN_END_ADDR 0xfa14
+#define STB0899_BASE_CN_END_ADDR 0x00000000
+#define STB0899_CN_END_ADDR (0x0fff << 0)
+#define STB0899_OFFST_CN_END_ADDR 0
+#define STB0899_WIDTH_CN_END_ADDR 12
+
+#define STB0899_OFF0_INFO_LENGTH 0xfa1c
+#define STB0899_BASE_INFO_LENGTH 0x00000000
+#define STB0899_INFO_LENGTH (0xff << 0)
+#define STB0899_OFFST_INFO_LENGTH 0
+#define STB0899_WIDTH_INFO_LENGTH 8
+
+#define STB0899_OFF0_BOT_ADDR 0xfa20
+#define STB0899_BASE_BOT_ADDR 0x00000000
+#define STB0899_BOTTOM_BASE_ADDR (0x03ff << 0)
+#define STB0899_OFFST_BOTTOM_BASE_ADDR 0
+#define STB0899_WIDTH_BOTTOM_BASE_ADDR 10
+
+#define STB0899_OFF0_BCH_BLK_LN 0xfa24
+#define STB0899_BASE_BCH_BLK_LN 0x00000000
+#define STB0899_BCH_BLOCK_LENGTH (0xffff << 0)
+#define STB0899_OFFST_BCH_BLOCK_LENGTH 0
+#define STB0899_WIDTH_BCH_BLOCK_LENGTH 16
+
+#define STB0899_OFF0_BCH_T 0xfa28
+#define STB0899_BASE_BCH_T 0x00000000
+#define STB0899_BCH_T (0x0f << 0)
+#define STB0899_OFFST_BCH_T 0
+#define STB0899_WIDTH_BCH_T 4
+
+#define STB0899_OFF0_CNFG_MODE 0xfa00
+#define STB0899_BASE_CNFG_MODE 0x00000800
+#define STB0899_MODCOD (0x1f << 2)
+#define STB0899_OFFST_MODCOD 2
+#define STB0899_WIDTH_MODCOD 5
+#define STB0899_MODCOD_SEL (0x01 << 1)
+#define STB0899_OFFST_MODCOD_SEL 1
+#define STB0899_WIDTH_MODCOD_SEL 1
+#define STB0899_CONFIG_MODE (0x01 << 0)
+#define STB0899_OFFST_CONFIG_MODE 0
+#define STB0899_WIDTH_CONFIG_MODE 1
+
+#define STB0899_OFF0_LDPC_STAT 0xfa04
+#define STB0899_BASE_LDPC_STAT 0x00000800
+#define STB0899_ITERATION (0xff << 3)
+#define STB0899_OFFST_ITERATION 3
+#define STB0899_WIDTH_ITERATION 8
+#define STB0899_LDPC_DEC_STATE (0x07 << 0)
+#define STB0899_OFFST_LDPC_DEC_STATE 0
+#define STB0899_WIDTH_LDPC_DEC_STATE 3
+
+#define STB0899_OFF0_ITER_SCALE 0xfa08
+#define STB0899_BASE_ITER_SCALE 0x00000800
+#define STB0899_ITERATION_SCALE (0xff << 0)
+#define STB0899_OFFST_ITERATION_SCALE 0
+#define STB0899_WIDTH_ITERATION_SCALE 8
+
+#define STB0899_OFF0_INPUT_MODE 0xfa0c
+#define STB0899_BASE_INPUT_MODE 0x00000800
+#define STB0899_SD_BLOCK1_STREAM0 (0x01 << 0)
+#define STB0899_OFFST_SD_BLOCK1_STREAM0 0
+#define STB0899_WIDTH_SD_BLOCK1_STREAM0 1
+
+#define STB0899_OFF0_LDPCDECRST 0xfa10
+#define STB0899_BASE_LDPCDECRST 0x00000800
+#define STB0899_LDPC_DEC_RST (0x01 << 0)
+#define STB0899_OFFST_LDPC_DEC_RST 0
+#define STB0899_WIDTH_LDPC_DEC_RST 1
+
+#define STB0899_OFF0_CLK_PER_BYTE_RW 0xfa14
+#define STB0899_BASE_CLK_PER_BYTE_RW 0x00000800
+#define STB0899_CLKS_PER_BYTE (0x0f << 0)
+#define STB0899_OFFST_CLKS_PER_BYTE 0
+#define STB0899_WIDTH_CLKS_PER_BYTE 5
+
+#define STB0899_OFF0_BCH_ERRORS 0xfa18
+#define STB0899_BASE_BCH_ERRORS 0x00000800
+#define STB0899_BCH_ERRORS (0x0f << 0)
+#define STB0899_OFFST_BCH_ERRORS 0
+#define STB0899_WIDTH_BCH_ERRORS 4
+
+#define STB0899_OFF0_LDPC_ERRORS 0xfa1c
+#define STB0899_BASE_LDPC_ERRORS 0x00000800
+#define STB0899_LDPC_ERRORS (0xffff << 0)
+#define STB0899_OFFST_LDPC_ERRORS 0
+#define STB0899_WIDTH_LDPC_ERRORS 16
+
+#define STB0899_OFF0_BCH_MODE 0xfa20
+#define STB0899_BASE_BCH_MODE 0x00000800
+#define STB0899_BCH_CORRECT_N (0x01 << 1)
+#define STB0899_OFFST_BCH_CORRECT_N 1
+#define STB0899_WIDTH_BCH_CORRECT_N 1
+#define STB0899_FULL_BYPASS (0x01 << 0)
+#define STB0899_OFFST_FULL_BYPASS 0
+#define STB0899_WIDTH_FULL_BYPASS 1
+
+#define STB0899_OFF0_ERR_ACC_PER 0xfa24
+#define STB0899_BASE_ERR_ACC_PER 0x00000800
+#define STB0899_BCH_ERR_ACC_PERIOD (0x0f << 0)
+#define STB0899_OFFST_BCH_ERR_ACC_PERIOD 0
+#define STB0899_WIDTH_BCH_ERR_ACC_PERIOD 4
+
+#define STB0899_OFF0_BCH_ERR_ACC 0xfa28
+#define STB0899_BASE_BCH_ERR_ACC 0x00000800
+#define STB0899_BCH_ERR_ACCUM (0xff << 0)
+#define STB0899_OFFST_BCH_ERR_ACCUM 0
+#define STB0899_WIDTH_BCH_ERR_ACCUM 8
+
+#define STB0899_OFF0_FEC_CORE_ID_REG 0xfa2c
+#define STB0899_BASE_FEC_CORE_ID_REG 0x00000800
+#define STB0899_FEC_CORE_ID (0xffffffff << 0)
+#define STB0899_OFFST_FEC_CORE_ID 0
+#define STB0899_WIDTH_FEC_CORE_ID 32
+
+#define STB0899_OFF0_FEC_VER_ID_REG 0xfa34
+#define STB0899_BASE_FEC_VER_ID_REG 0x00000800
+#define STB0899_FEC_VER_ID (0xff << 0)
+#define STB0899_OFFST_FEC_VER_ID 0
+#define STB0899_WIDTH_FEC_VER_ID 8
+
+#define STB0899_OFF0_FEC_TP_SEL 0xfa38
+#define STB0899_BASE_FEC_TP_SEL 0x00000800
+
+#define STB0899_OFF0_CSM_CNTRL1 0xf310
+#define STB0899_BASE_CSM_CNTRL1 0x00000400
+#define STB0899_CSM_FORCE_FREQLOCK (0x01 << 19)
+#define STB0899_OFFST_CSM_FORCE_FREQLOCK 19
+#define STB0899_WIDTH_CSM_FORCE_FREQLOCK 1
+#define STB0899_CSM_FREQ_LOCKSTATE (0x01 << 18)
+#define STB0899_OFFST_CSM_FREQ_LOCKSTATE 18
+#define STB0899_WIDTH_CSM_FREQ_LOCKSTATE 1
+#define STB0899_CSM_AUTO_PARAM (0x01 << 17)
+#define STB0899_OFFST_CSM_AUTO_PARAM 17
+#define STB0899_WIDTH_CSM_AUTO_PARAM 1
+#define STB0899_FE_LOOP_SHIFT (0x07 << 14)
+#define STB0899_OFFST_FE_LOOP_SHIFT 14
+#define STB0899_WIDTH_FE_LOOP_SHIFT 3
+#define STB0899_CSM_AGC_SHIFT (0x07 << 11)
+#define STB0899_OFFST_CSM_AGC_SHIFT 11
+#define STB0899_WIDTH_CSM_AGC_SHIFT 3
+#define STB0899_CSM_AGC_GAIN (0x1ff << 2)
+#define STB0899_OFFST_CSM_AGC_GAIN 2
+#define STB0899_WIDTH_CSM_AGC_GAIN 9
+#define STB0899_CSM_TWO_PASS (0x01 << 1)
+#define STB0899_OFFST_CSM_TWO_PASS 1
+#define STB0899_WIDTH_CSM_TWO_PASS 1
+#define STB0899_CSM_DVT_TABLE (0x01 << 0)
+#define STB0899_OFFST_CSM_DVT_TABLE 0
+#define STB0899_WIDTH_CSM_DVT_TABLE 1
+
+#define STB0899_OFF0_CSM_CNTRL2 0xf314
+#define STB0899_BASE_CSM_CNTRL2 0x00000400
+#define STB0899_CSM_GAMMA_RHO_ACQ (0x1ff << 9)
+#define STB0899_OFFST_CSM_GAMMA_RHOACQ 9
+#define STB0899_WIDTH_CSM_GAMMA_RHOACQ 9
+#define STB0899_CSM_GAMMA_ACQ (0x1ff << 0)
+#define STB0899_OFFST_CSM_GAMMA_ACQ 0
+#define STB0899_WIDTH_CSM_GAMMA_ACQ 9
+
+#define STB0899_OFF0_CSM_CNTRL3 0xf318
+#define STB0899_BASE_CSM_CNTRL3 0x00000400
+#define STB0899_CSM_GAMMA_RHO_TRACK (0x1ff << 9)
+#define STB0899_OFFST_CSM_GAMMA_RHOTRACK 9
+#define STB0899_WIDTH_CSM_GAMMA_RHOTRACK 9
+#define STB0899_CSM_GAMMA_TRACK (0x1ff << 0)
+#define STB0899_OFFST_CSM_GAMMA_TRACK 0
+#define STB0899_WIDTH_CSM_GAMMA_TRACK 9
+
+#define STB0899_OFF0_CSM_CNTRL4 0xf31c
+#define STB0899_BASE_CSM_CNTRL4 0x00000400
+#define STB0899_CSM_PHASEDIFF_THRESH (0x0f << 8)
+#define STB0899_OFFST_CSM_PHASEDIFF_THRESH 8
+#define STB0899_WIDTH_CSM_PHASEDIFF_THRESH 4
+#define STB0899_CSM_LOCKCOUNT_THRESH (0xff << 0)
+#define STB0899_OFFST_CSM_LOCKCOUNT_THRESH 0
+#define STB0899_WIDTH_CSM_LOCKCOUNT_THRESH 8
+
+/* Check on chapter 8 page 42 */
+#define STB0899_ERRCTRL1 0xf574
+#define STB0899_ERRCTRL2 0xf575
+#define STB0899_ERRCTRL3 0xf576
+#define STB0899_ERR_SRC_S1 (0x1f << 3)
+#define STB0899_OFFST_ERR_SRC_S1 3
+#define STB0899_WIDTH_ERR_SRC_S1 5
+#define STB0899_ERR_SRC_S2 (0x0f << 0)
+#define STB0899_OFFST_ERR_SRC_S2 0
+#define STB0899_WIDTH_ERR_SRC_S2 4
+#define STB0899_NOE (0x07 << 0)
+#define STB0899_OFFST_NOE 0
+#define STB0899_WIDTH_NOE 3
+
+#define STB0899_ECNT1M 0xf524
+#define STB0899_ECNT1L 0xf525
+#define STB0899_ECNT2M 0xf526
+#define STB0899_ECNT2L 0xf527
+#define STB0899_ECNT3M 0xf528
+#define STB0899_ECNT3L 0xf529
+
+#define STB0899_DMONMSK1 0xf57b
+#define STB0899_DMONMSK1_WAIT_1STEP (1 << 7)
+#define STB0899_DMONMSK1_FREE_14 (1 << 6)
+#define STB0899_DMONMSK1_AVRGVIT_CALC (1 << 5)
+#define STB0899_DMONMSK1_FREE_12 (1 << 4)
+#define STB0899_DMONMSK1_FREE_11 (1 << 3)
+#define STB0899_DMONMSK1_B0DIV_CALC (1 << 2)
+#define STB0899_DMONMSK1_KDIVB1_CALC (1 << 1)
+#define STB0899_DMONMSK1_KDIVB2_CALC (1 << 0)
+
+#define STB0899_DMONMSK0 0xf57c
+#define STB0899_DMONMSK0_SMOTTH_CALC (1 << 7)
+#define STB0899_DMONMSK0_FREE_6 (1 << 6)
+#define STB0899_DMONMSK0_SIGPOWER_CALC (1 << 5)
+#define STB0899_DMONMSK0_QSEUIL_CALC (1 << 4)
+#define STB0899_DMONMSK0_FREE_3 (1 << 3)
+#define STB0899_DMONMSK0_FREE_2 (1 << 2)
+#define STB0899_DMONMSK0_KVDIVB1_CALC (1 << 1)
+#define STB0899_DMONMSK0_KVDIVB2_CALC (1 << 0)
+
+#define STB0899_TSULC 0xf549
+#define STB0899_ULNOSYNCBYTES (0x01 << 7)
+#define STB0899_OFFST_ULNOSYNCBYTES 7
+#define STB0899_WIDTH_ULNOSYNCBYTES 1
+#define STB0899_ULPARITY_ON (0x01 << 6)
+#define STB0899_OFFST_ULPARITY_ON 6
+#define STB0899_WIDTH_ULPARITY_ON 1
+#define STB0899_ULSYNCOUTRS (0x01 << 5)
+#define STB0899_OFFST_ULSYNCOUTRS 5
+#define STB0899_WIDTH_ULSYNCOUTRS 1
+#define STB0899_ULDSS_PACKETS (0x01 << 0)
+#define STB0899_OFFST_ULDSS_PACKETS 0
+#define STB0899_WIDTH_ULDSS_PACKETS 1
+
+#define STB0899_TSLPL 0xf54b
+#define STB0899_LLDVBS2_MODE (0x01 << 4)
+#define STB0899_OFFST_LLDVBS2_MODE 4
+#define STB0899_WIDTH_LLDVBS2_MODE 1
+#define STB0899_LLISSYI_ON (0x01 << 3)
+#define STB0899_OFFST_LLISSYI_ON 3
+#define STB0899_WIDTH_LLISSYI_ON 1
+#define STB0899_LLNPD_ON (0x01 << 2)
+#define STB0899_OFFST_LLNPD_ON 2
+#define STB0899_WIDTH_LLNPD_ON 1
+#define STB0899_LLCRC8_ON (0x01 << 1)
+#define STB0899_OFFST_LLCRC8_ON 1
+#define STB0899_WIDTH_LLCRC8_ON 1
+
+#define STB0899_TSCFGH 0xf54c
+#define STB0899_OUTRS_PS (0x01 << 6)
+#define STB0899_OFFST_OUTRS_PS 6
+#define STB0899_WIDTH_OUTRS_PS 1
+#define STB0899_SYNCBYTE (0x01 << 5)
+#define STB0899_OFFST_SYNCBYTE 5
+#define STB0899_WIDTH_SYNCBYTE 1
+#define STB0899_PFBIT (0x01 << 4)
+#define STB0899_OFFST_PFBIT 4
+#define STB0899_WIDTH_PFBIT 1
+#define STB0899_ERR_BIT (0x01 << 3)
+#define STB0899_OFFST_ERR_BIT 3
+#define STB0899_WIDTH_ERR_BIT 1
+#define STB0899_MPEG (0x01 << 2)
+#define STB0899_OFFST_MPEG 2
+#define STB0899_WIDTH_MPEG 1
+#define STB0899_CLK_POL (0x01 << 1)
+#define STB0899_OFFST_CLK_POL 1
+#define STB0899_WIDTH_CLK_POL 1
+#define STB0899_FORCE0 (0x01 << 0)
+#define STB0899_OFFST_FORCE0 0
+#define STB0899_WIDTH_FORCE0 1
+
+#define STB0899_TSCFGM 0xf54d
+#define STB0899_LLPRIORITY (0x01 << 3)
+#define STB0899_OFFST_LLPRIORIY 3
+#define STB0899_WIDTH_LLPRIORITY 1
+#define STB0899_EN188 (0x01 << 2)
+#define STB0899_OFFST_EN188 2
+#define STB0899_WIDTH_EN188 1
+
+#define STB0899_TSCFGL 0xf54e
+#define STB0899_DEL_ERRPCK (0x01 << 7)
+#define STB0899_OFFST_DEL_ERRPCK 7
+#define STB0899_WIDTH_DEL_ERRPCK 1
+#define STB0899_ERRFLAGSTD (0x01 << 5)
+#define STB0899_OFFST_ERRFLAGSTD 5
+#define STB0899_WIDTH_ERRFLAGSTD 1
+#define STB0899_MPEGERR (0x01 << 4)
+#define STB0899_OFFST_MPEGERR 4
+#define STB0899_WIDTH_MPEGERR 1
+#define STB0899_BCH_CHK (0x01 << 3)
+#define STB0899_OFFST_BCH_CHK 5
+#define STB0899_WIDTH_BCH_CHK 1
+#define STB0899_CRC8CHK (0x01 << 2)
+#define STB0899_OFFST_CRC8CHK 2
+#define STB0899_WIDTH_CRC8CHK 1
+#define STB0899_SPEC_INFO (0x01 << 1)
+#define STB0899_OFFST_SPEC_INFO 1
+#define STB0899_WIDTH_SPEC_INFO 1
+#define STB0899_LOW_PRIO_CLK (0x01 << 0)
+#define STB0899_OFFST_LOW_PRIO_CLK 0
+#define STB0899_WIDTH_LOW_PRIO_CLK 1
+#define STB0899_ERROR_NORM (0x00 << 0)
+#define STB0899_OFFST_ERROR_NORM 0
+#define STB0899_WIDTH_ERROR_NORM 0
+
+#define STB0899_TSOUT 0xf54f
+#define STB0899_RSSYNCDEL 0xf550
+#define STB0899_TSINHDELH 0xf551
+#define STB0899_TSINHDELM 0xf552
+#define STB0899_TSINHDELL 0xf553
+#define STB0899_TSLLSTKM 0xf55a
+#define STB0899_TSLLSTKL 0xf55b
+#define STB0899_TSULSTKM 0xf55c
+#define STB0899_TSULSTKL 0xf55d
+#define STB0899_TSSTATUS 0xf561
+
+#define STB0899_PDELCTRL 0xf600
+#define STB0899_INVERT_RES (0x01 << 7)
+#define STB0899_OFFST_INVERT_RES 7
+#define STB0899_WIDTH_INVERT_RES 1
+#define STB0899_FORCE_ACCEPTED (0x01 << 6)
+#define STB0899_OFFST_FORCE_ACCEPTED 6
+#define STB0899_WIDTH_FORCE_ACCEPTED 1
+#define STB0899_FILTER_EN (0x01 << 5)
+#define STB0899_OFFST_FILTER_EN 5
+#define STB0899_WIDTH_FILTER_EN 1
+#define STB0899_LOCKFALL_THRESH (0x01 << 4)
+#define STB0899_OFFST_LOCKFALL_THRESH 4
+#define STB0899_WIDTH_LOCKFALL_THRESH 1
+#define STB0899_HYST_EN (0x01 << 3)
+#define STB0899_OFFST_HYST_EN 3
+#define STB0899_WIDTH_HYST_EN 1
+#define STB0899_HYST_SWRST (0x01 << 2)
+#define STB0899_OFFST_HYST_SWRST 2
+#define STB0899_WIDTH_HYST_SWRST 1
+#define STB0899_ALGO_EN (0x01 << 1)
+#define STB0899_OFFST_ALGO_EN 1
+#define STB0899_WIDTH_ALGO_EN 1
+#define STB0899_ALGO_SWRST (0x01 << 0)
+#define STB0899_OFFST_ALGO_SWRST 0
+#define STB0899_WIDTH_ALGO_SWRST 1
+
+#define STB0899_PDELCTRL2 0xf601
+#define STB0899_BBHCTRL1 0xf602
+#define STB0899_BBHCTRL2 0xf603
+#define STB0899_HYSTTHRESH 0xf604
+
+#define STB0899_MATCSTM 0xf605
+#define STB0899_MATCSTL 0xf606
+#define STB0899_UPLCSTM 0xf607
+#define STB0899_UPLCSTL 0xf608
+#define STB0899_DFLCSTM 0xf609
+#define STB0899_DFLCSTL 0xf60a
+#define STB0899_SYNCCST 0xf60b
+#define STB0899_SYNCDCSTM 0xf60c
+#define STB0899_SYNCDCSTL 0xf60d
+#define STB0899_ISI_ENTRY 0xf60e
+#define STB0899_ISI_BIT_EN 0xf60f
+#define STB0899_MATSTRM 0xf610
+#define STB0899_MATSTRL 0xf611
+#define STB0899_UPLSTRM 0xf612
+#define STB0899_UPLSTRL 0xf613
+#define STB0899_DFLSTRM 0xf614
+#define STB0899_DFLSTRL 0xf615
+#define STB0899_SYNCSTR 0xf616
+#define STB0899_SYNCDSTRM 0xf617
+#define STB0899_SYNCDSTRL 0xf618
+
+#define STB0899_CFGPDELSTATUS1 0xf619
+#define STB0899_BADDFL (0x01 << 6)
+#define STB0899_OFFST_BADDFL 6
+#define STB0899_WIDTH_BADDFL 1
+#define STB0899_CONTINUOUS_STREAM (0x01 << 5)
+#define STB0899_OFFST_CONTINUOUS_STREAM 5
+#define STB0899_WIDTH_CONTINUOUS_STREAM 1
+#define STB0899_ACCEPTED_STREAM (0x01 << 4)
+#define STB0899_OFFST_ACCEPTED_STREAM 4
+#define STB0899_WIDTH_ACCEPTED_STREAM 1
+#define STB0899_BCH_ERRFLAG (0x01 << 3)
+#define STB0899_OFFST_BCH_ERRFLAG 3
+#define STB0899_WIDTH_BCH_ERRFLAG 1
+#define STB0899_CRCRES (0x01 << 2)
+#define STB0899_OFFST_CRCRES 2
+#define STB0899_WIDTH_CRCRES 1
+#define STB0899_CFGPDELSTATUS_LOCK (0x01 << 1)
+#define STB0899_OFFST_CFGPDELSTATUS_LOCK 1
+#define STB0899_WIDTH_CFGPDELSTATUS_LOCK 1
+#define STB0899_1STLOCK (0x01 << 0)
+#define STB0899_OFFST_1STLOCK 0
+#define STB0899_WIDTH_1STLOCK 1
+
+#define STB0899_CFGPDELSTATUS2 0xf61a
+#define STB0899_BBFERRORM 0xf61b
+#define STB0899_BBFERRORL 0xf61c
+#define STB0899_UPKTERRORM 0xf61d
+#define STB0899_UPKTERRORL 0xf61e
+
+#define STB0899_TSTCK 0xff10
+
+#define STB0899_TSTRES 0xff11
+#define STB0899_FRESLDPC (0x01 << 7)
+#define STB0899_OFFST_FRESLDPC 7
+#define STB0899_WIDTH_FRESLDPC 1
+#define STB0899_FRESRS (0x01 << 6)
+#define STB0899_OFFST_FRESRS 6
+#define STB0899_WIDTH_FRESRS 1
+#define STB0899_FRESVIT (0x01 << 5)
+#define STB0899_OFFST_FRESVIT 5
+#define STB0899_WIDTH_FRESVIT 1
+#define STB0899_FRESMAS1_2 (0x01 << 4)
+#define STB0899_OFFST_FRESMAS1_2 4
+#define STB0899_WIDTH_FRESMAS1_2 1
+#define STB0899_FRESACS (0x01 << 3)
+#define STB0899_OFFST_FRESACS 3
+#define STB0899_WIDTH_FRESACS 1
+#define STB0899_FRESSYM (0x01 << 2)
+#define STB0899_OFFST_FRESSYM 2
+#define STB0899_WIDTH_FRESSYM 1
+#define STB0899_FRESMAS (0x01 << 1)
+#define STB0899_OFFST_FRESMAS 1
+#define STB0899_WIDTH_FRESMAS 1
+#define STB0899_FRESINT (0x01 << 0)
+#define STB0899_OFFST_FRESINIT 0
+#define STB0899_WIDTH_FRESINIT 1
+
+#define STB0899_TSTOUT 0xff12
+#define STB0899_EN_SIGNATURE (0x01 << 7)
+#define STB0899_OFFST_EN_SIGNATURE 7
+#define STB0899_WIDTH_EN_SIGNATURE 1
+#define STB0899_BCLK_CLK (0x01 << 6)
+#define STB0899_OFFST_BCLK_CLK 6
+#define STB0899_WIDTH_BCLK_CLK 1
+#define STB0899_SGNL_OUT (0x01 << 5)
+#define STB0899_OFFST_SGNL_OUT 5
+#define STB0899_WIDTH_SGNL_OUT 1
+#define STB0899_TS (0x01 << 4)
+#define STB0899_OFFST_TS 4
+#define STB0899_WIDTH_TS 1
+#define STB0899_CTEST (0x01 << 0)
+#define STB0899_OFFST_CTEST 0
+#define STB0899_WIDTH_CTEST 1
+
+#define STB0899_TSTIN 0xff13
+#define STB0899_TEST_IN (0x01 << 7)
+#define STB0899_OFFST_TEST_IN 7
+#define STB0899_WIDTH_TEST_IN 1
+#define STB0899_EN_ADC (0x01 << 6)
+#define STB0899_OFFST_EN_ADC 6
+#define STB0899_WIDTH_ENADC 1
+#define STB0899_SGN_ADC (0x01 << 5)
+#define STB0899_OFFST_SGN_ADC 5
+#define STB0899_WIDTH_SGN_ADC 1
+#define STB0899_BCLK_IN (0x01 << 4)
+#define STB0899_OFFST_BCLK_IN 4
+#define STB0899_WIDTH_BCLK_IN 1
+#define STB0899_JETONIN_MODE (0x01 << 3)
+#define STB0899_OFFST_JETONIN_MODE 3
+#define STB0899_WIDTH_JETONIN_MODE 1
+#define STB0899_BCLK_VALUE (0x01 << 2)
+#define STB0899_OFFST_BCLK_VALUE 2
+#define STB0899_WIDTH_BCLK_VALUE 1
+#define STB0899_SGNRST_T12 (0x01 << 1)
+#define STB0899_OFFST_SGNRST_T12 1
+#define STB0899_WIDTH_SGNRST_T12 1
+#define STB0899_LOWSP_ENAX (0x01 << 0)
+#define STB0899_OFFST_LOWSP_ENAX 0
+#define STB0899_WIDTH_LOWSP_ENAX 1
+
+#define STB0899_TSTSYS 0xff14
+#define STB0899_TSTCHIP 0xff15
+#define STB0899_TSTFREE 0xff16
+#define STB0899_TSTI2C 0xff17
+#define STB0899_BITSPEEDM 0xff1c
+#define STB0899_BITSPEEDL 0xff1d
+#define STB0899_TBUSBIT 0xff1e
+#define STB0899_TSTDIS 0xff24
+#define STB0899_TSTDISRX 0xff25
+#define STB0899_TSTJETON 0xff28
+#define STB0899_TSTDCADJ 0xff40
+#define STB0899_TSTAGC1 0xff41
+#define STB0899_TSTAGC1N 0xff42
+#define STB0899_TSTPOLYPH 0xff48
+#define STB0899_TSTR 0xff49
+#define STB0899_TSTAGC2 0xff4a
+#define STB0899_TSTCTL1 0xff4b
+#define STB0899_TSTCTL2 0xff4c
+#define STB0899_TSTCTL3 0xff4d
+#define STB0899_TSTDEMAP 0xff50
+#define STB0899_TSTDEMAP2 0xff51
+#define STB0899_TSTDEMMON 0xff52
+#define STB0899_TSTRATE 0xff53
+#define STB0899_TSTSELOUT 0xff54
+#define STB0899_TSYNC 0xff55
+#define STB0899_TSTERR 0xff56
+#define STB0899_TSTRAM1 0xff58
+#define STB0899_TSTVSELOUT 0xff59
+#define STB0899_TSTFORCEIN 0xff5a
+#define STB0899_TSTRS1 0xff5c
+#define STB0899_TSTRS2 0xff5d
+#define STB0899_TSTRS3 0xff53
+
+#define STB0899_INTBUFSTATUS 0xf200
+#define STB0899_INTBUFCTRL 0xf201
+#define STB0899_PCKLENUL 0xf55e
+#define STB0899_PCKLENLL 0xf55f
+#define STB0899_RSPCKLEN 0xf560
+
+/* 2 registers */
+#define STB0899_SYNCDCST 0xf60c
+
+/* DiSEqC */
+#define STB0899_DISCNTRL1 0xf0a0
+#define STB0899_TIMOFF (0x01 << 7)
+#define STB0899_OFFST_TIMOFF 7
+#define STB0899_WIDTH_TIMOFF 1
+#define STB0899_DISEQCRESET (0x01 << 6)
+#define STB0899_OFFST_DISEQCRESET 6
+#define STB0899_WIDTH_DISEQCRESET 1
+#define STB0899_TIMCMD (0x03 << 4)
+#define STB0899_OFFST_TIMCMD 4
+#define STB0899_WIDTH_TIMCMD 2
+#define STB0899_DISPRECHARGE (0x01 << 2)
+#define STB0899_OFFST_DISPRECHARGE 2
+#define STB0899_WIDTH_DISPRECHARGE 1
+#define STB0899_DISEQCMODE (0x03 << 0)
+#define STB0899_OFFST_DISEQCMODE 0
+#define STB0899_WIDTH_DISEQCMODE 2
+
+#define STB0899_DISCNTRL2 0xf0a1
+#define STB0899_RECEIVER_ON (0x01 << 7)
+#define STB0899_OFFST_RECEIVER_ON 7
+#define STB0899_WIDTH_RECEIVER_ON 1
+#define STB0899_IGNO_SHORT_22K (0x01 << 6)
+#define STB0899_OFFST_IGNO_SHORT_22K 6
+#define STB0899_WIDTH_IGNO_SHORT_22K 1
+#define STB0899_ONECHIP_TRX (0x01 << 5)
+#define STB0899_OFFST_ONECHIP_TRX 5
+#define STB0899_WIDTH_ONECHIP_TRX 1
+#define STB0899_EXT_ENVELOP (0x01 << 4)
+#define STB0899_OFFST_EXT_ENVELOP 4
+#define STB0899_WIDTH_EXT_ENVELOP 1
+#define STB0899_PIN_SELECT (0x03 << 2)
+#define STB0899_OFFST_PIN_SELCT 2
+#define STB0899_WIDTH_PIN_SELCT 2
+#define STB0899_IRQ_RXEND (0x01 << 1)
+#define STB0899_OFFST_IRQ_RXEND 1
+#define STB0899_WIDTH_IRQ_RXEND 1
+#define STB0899_IRQ_4NBYTES (0x01 << 0)
+#define STB0899_OFFST_IRQ_4NBYTES 0
+#define STB0899_WIDTH_IRQ_4NBYTES 1
+
+#define STB0899_DISRX_ST0 0xf0a4
+#define STB0899_RXEND (0x01 << 7)
+#define STB0899_OFFST_RXEND 7
+#define STB0899_WIDTH_RXEND 1
+#define STB0899_RXACTIVE (0x01 << 6)
+#define STB0899_OFFST_RXACTIVE 6
+#define STB0899_WIDTH_RXACTIVE 1
+#define STB0899_SHORT22K (0x01 << 5)
+#define STB0899_OFFST_SHORT22K 5
+#define STB0899_WIDTH_SHORT22K 1
+#define STB0899_CONTTONE (0x01 << 4)
+#define STB0899_OFFST_CONTTONE 4
+#define STB0899_WIDTH_CONTONE 1
+#define STB0899_4BFIFOREDY (0x01 << 3)
+#define STB0899_OFFST_4BFIFOREDY 3
+#define STB0899_WIDTH_4BFIFOREDY 1
+#define STB0899_FIFOEMPTY (0x01 << 2)
+#define STB0899_OFFST_FIFOEMPTY 2
+#define STB0899_WIDTH_FIFOEMPTY 1
+#define STB0899_ABORTTRX (0x01 << 0)
+#define STB0899_OFFST_ABORTTRX 0
+#define STB0899_WIDTH_ABORTTRX 1
+
+#define STB0899_DISRX_ST1 0xf0a5
+#define STB0899_RXFAIL (0x01 << 7)
+#define STB0899_OFFST_RXFAIL 7
+#define STB0899_WIDTH_RXFAIL 1
+#define STB0899_FIFOPFAIL (0x01 << 6)
+#define STB0899_OFFST_FIFOPFAIL 6
+#define STB0899_WIDTH_FIFOPFAIL 1
+#define STB0899_RXNONBYTES (0x01 << 5)
+#define STB0899_OFFST_RXNONBYTES 5
+#define STB0899_WIDTH_RXNONBYTES 1
+#define STB0899_FIFOOVF (0x01 << 4)
+#define STB0899_OFFST_FIFOOVF 4
+#define STB0899_WIDTH_FIFOOVF 1
+#define STB0899_FIFOBYTENBR (0x0f << 0)
+#define STB0899_OFFST_FIFOBYTENBR 0
+#define STB0899_WIDTH_FIFOBYTENBR 4
+
+#define STB0899_DISPARITY 0xf0a6
+
+#define STB0899_DISFIFO 0xf0a7
+
+#define STB0899_DISSTATUS 0xf0a8
+#define STB0899_FIFOFULL (0x01 << 6)
+#define STB0899_OFFST_FIFOFULL 6
+#define STB0899_WIDTH_FIFOFULL 1
+#define STB0899_TXIDLE (0x01 << 5)
+#define STB0899_OFFST_TXIDLE 5
+#define STB0899_WIDTH_TXIDLE 1
+#define STB0899_GAPBURST (0x01 << 4)
+#define STB0899_OFFST_GAPBURST 4
+#define STB0899_WIDTH_GAPBURST 1
+#define STB0899_TXFIFOBYTES (0x0f << 0)
+#define STB0899_OFFST_TXFIFOBYTES 0
+#define STB0899_WIDTH_TXFIFOBYTES 4
+#define STB0899_DISF22 0xf0a9
+
+#define STB0899_DISF22RX 0xf0aa
+
+/* General Purpose */
+#define STB0899_SYSREG 0xf101
+#define STB0899_ACRPRESC 0xf110
+#define STB0899_OFFST_RSVD2 7
+#define STB0899_WIDTH_RSVD2 1
+#define STB0899_OFFST_ACRPRESC 4
+#define STB0899_WIDTH_ACRPRESC 3
+#define STB0899_OFFST_RSVD1 3
+#define STB0899_WIDTH_RSVD1 1
+#define STB0899_OFFST_ACRPRESC2 0
+#define STB0899_WIDTH_ACRPRESC2 3
+
+#define STB0899_ACRDIV1 0xf111
+#define STB0899_ACRDIV2 0xf112
+#define STB0899_DACR1 0xf113
+#define STB0899_DACR2 0xf114
+#define STB0899_OUTCFG 0xf11c
+#define STB0899_MODECFG 0xf11d
+#define STB0899_NCOARSE 0xf1b3
+
+#define STB0899_SYNTCTRL 0xf1b6
+#define STB0899_STANDBY (0x01 << 7)
+#define STB0899_OFFST_STANDBY 7
+#define STB0899_WIDTH_STANDBY 1
+#define STB0899_BYPASSPLL (0x01 << 6)
+#define STB0899_OFFST_BYPASSPLL 6
+#define STB0899_WIDTH_BYPASSPLL 1
+#define STB0899_SEL1XRATIO (0x01 << 5)
+#define STB0899_OFFST_SEL1XRATIO 5
+#define STB0899_WIDTH_SEL1XRATIO 1
+#define STB0899_SELOSCI (0x01 << 1)
+#define STB0899_OFFST_SELOSCI 1
+#define STB0899_WIDTH_SELOSCI 1
+
+#define STB0899_FILTCTRL 0xf1b7
+#define STB0899_SYSCTRL 0xf1b8
+
+#define STB0899_STOPCLK1 0xf1c2
+#define STB0899_STOP_CKINTBUF108 (0x01 << 7)
+#define STB0899_OFFST_STOP_CKINTBUF108 7
+#define STB0899_WIDTH_STOP_CKINTBUF108 1
+#define STB0899_STOP_CKINTBUF216 (0x01 << 6)
+#define STB0899_OFFST_STOP_CKINTBUF216 6
+#define STB0899_WIDTH_STOP_CKINTBUF216 1
+#define STB0899_STOP_CHK8PSK (0x01 << 5)
+#define STB0899_OFFST_STOP_CHK8PSK 5
+#define STB0899_WIDTH_STOP_CHK8PSK 1
+#define STB0899_STOP_CKFEC108 (0x01 << 4)
+#define STB0899_OFFST_STOP_CKFEC108 4
+#define STB0899_WIDTH_STOP_CKFEC108 1
+#define STB0899_STOP_CKFEC216 (0x01 << 3)
+#define STB0899_OFFST_STOP_CKFEC216 3
+#define STB0899_WIDTH_STOP_CKFEC216 1
+#define STB0899_STOP_CKCORE216 (0x01 << 2)
+#define STB0899_OFFST_STOP_CKCORE216 2
+#define STB0899_WIDTH_STOP_CKCORE216 1
+#define STB0899_STOP_CKADCI108 (0x01 << 1)
+#define STB0899_OFFST_STOP_CKADCI108 1
+#define STB0899_WIDTH_STOP_CKADCI108 1
+#define STB0899_STOP_INVCKADCI108 (0x01 << 0)
+#define STB0899_OFFST_STOP_INVCKADCI108 0
+#define STB0899_WIDTH_STOP_INVCKADCI108 1
+
+#define STB0899_STOPCLK2 0xf1c3
+#define STB0899_STOP_CKS2DMD108 (0x01 << 2)
+#define STB0899_OFFST_STOP_CKS2DMD108 2
+#define STB0899_WIDTH_STOP_CKS2DMD108 1
+#define STB0899_STOP_CKPKDLIN108 (0x01 << 1)
+#define STB0899_OFFST_STOP_CKPKDLIN108 1
+#define STB0899_WIDTH_STOP_CKPKDLIN108 1
+#define STB0899_STOP_CKPKDLIN216 (0x01 << 0)
+#define STB0899_OFFST_STOP_CKPKDLIN216 0
+#define STB0899_WIDTH_STOP_CKPKDLIN216 1
+
+#define STB0899_TSTTNR1 0xf1e0
+#define STB0899_BYPASS_ADC (0x01 << 7)
+#define STB0899_OFFST_BYPASS_ADC 7
+#define STB0899_WIDTH_BYPASS_ADC 1
+#define STB0899_INVADCICKOUT (0x01 << 6)
+#define STB0899_OFFST_INVADCICKOUT 6
+#define STB0899_WIDTH_INVADCICKOUT 1
+#define STB0899_ADCTEST_VOLTAGE (0x03 << 4)
+#define STB0899_OFFST_ADCTEST_VOLTAGE 4
+#define STB0899_WIDTH_ADCTEST_VOLTAGE 1
+#define STB0899_ADC_RESET (0x01 << 3)
+#define STB0899_OFFST_ADC_RESET 3
+#define STB0899_WIDTH_ADC_RESET 1
+#define STB0899_TSTTNR1_2 (0x01 << 2)
+#define STB0899_OFFST_TSTTNR1_2 2
+#define STB0899_WIDTH_TSTTNR1_2 1
+#define STB0899_ADCPON (0x01 << 1)
+#define STB0899_OFFST_ADCPON 1
+#define STB0899_WIDTH_ADCPON 1
+#define STB0899_ADCIN_MODE (0x01 << 0)
+#define STB0899_OFFST_ADCIN_MODE 0
+#define STB0899_WIDTH_ADCIN_MODE 1
+
+#define STB0899_TSTTNR2 0xf1e1
+#define STB0899_TSTTNR2_7 (0x01 << 7)
+#define STB0899_OFFST_TSTTNR2_7 7
+#define STB0899_WIDTH_TSTTNR2_7 1
+#define STB0899_NOT_DISRX_WIRED (0x01 << 6)
+#define STB0899_OFFST_NOT_DISRX_WIRED 6
+#define STB0899_WIDTH_NOT_DISRX_WIRED 1
+#define STB0899_DISEQC_DCURRENT (0x01 << 5)
+#define STB0899_OFFST_DISEQC_DCURRENT 5
+#define STB0899_WIDTH_DISEQC_DCURRENT 1
+#define STB0899_DISEQC_ZCURRENT (0x01 << 4)
+#define STB0899_OFFST_DISEQC_ZCURRENT 4
+#define STB0899_WIDTH_DISEQC_ZCURRENT 1
+#define STB0899_DISEQC_SINC_SOURCE (0x03 << 2)
+#define STB0899_OFFST_DISEQC_SINC_SOURCE 2
+#define STB0899_WIDTH_DISEQC_SINC_SOURCE 2
+#define STB0899_SELIQSRC (0x03 << 0)
+#define STB0899_OFFST_SELIQSRC 0
+#define STB0899_WIDTH_SELIQSRC 2
+
+#define STB0899_TSTTNR3 0xf1e2
+
+#define STB0899_I2CCFG 0xf129
+#define STB0899_I2CCFGRSVD (0x0f << 4)
+#define STB0899_OFFST_I2CCFGRSVD 4
+#define STB0899_WIDTH_I2CCFGRSVD 4
+#define STB0899_I2CFASTMODE (0x01 << 3)
+#define STB0899_OFFST_I2CFASTMODE 3
+#define STB0899_WIDTH_I2CFASTMODE 1
+#define STB0899_STATUSWR (0x01 << 2)
+#define STB0899_OFFST_STATUSWR 2
+#define STB0899_WIDTH_STATUSWR 1
+#define STB0899_I2CADDRINC (0x03 << 0)
+#define STB0899_OFFST_I2CADDRINC 0
+#define STB0899_WIDTH_I2CADDRINC 2
+
+#define STB0899_I2CRPT 0xf12a
+#define STB0899_I2CTON (0x01 << 7)
+#define STB0899_OFFST_I2CTON 7
+#define STB0899_WIDTH_I2CTON 1
+#define STB0899_ENARPTLEVEL (0x01 << 6)
+#define STB0899_OFFST_ENARPTLEVEL 6
+#define STB0899_WIDTH_ENARPTLEVEL 2
+#define STB0899_SCLTDELAY (0x01 << 3)
+#define STB0899_OFFST_SCLTDELAY 3
+#define STB0899_WIDTH_SCLTDELAY 1
+#define STB0899_STOPENA (0x01 << 2)
+#define STB0899_OFFST_STOPENA 2
+#define STB0899_WIDTH_STOPENA 1
+#define STB0899_STOPSDAT2SDA (0x01 << 1)
+#define STB0899_OFFST_STOPSDAT2SDA 1
+#define STB0899_WIDTH_STOPSDAT2SDA 1
+
+#define STB0899_IOPVALUE8 0xf136
+#define STB0899_IOPVALUE7 0xf137
+#define STB0899_IOPVALUE6 0xf138
+#define STB0899_IOPVALUE5 0xf139
+#define STB0899_IOPVALUE4 0xf13a
+#define STB0899_IOPVALUE3 0xf13b
+#define STB0899_IOPVALUE2 0xf13c
+#define STB0899_IOPVALUE1 0xf13d
+#define STB0899_IOPVALUE0 0xf13e
+
+#define STB0899_GPIO00CFG 0xf140
+
+#define STB0899_GPIO01CFG 0xf141
+#define STB0899_GPIO02CFG 0xf142
+#define STB0899_GPIO03CFG 0xf143
+#define STB0899_GPIO04CFG 0xf144
+#define STB0899_GPIO05CFG 0xf145
+#define STB0899_GPIO06CFG 0xf146
+#define STB0899_GPIO07CFG 0xf147
+#define STB0899_GPIO08CFG 0xf148
+#define STB0899_GPIO09CFG 0xf149
+#define STB0899_GPIO10CFG 0xf14a
+#define STB0899_GPIO11CFG 0xf14b
+#define STB0899_GPIO12CFG 0xf14c
+#define STB0899_GPIO13CFG 0xf14d
+#define STB0899_GPIO14CFG 0xf14e
+#define STB0899_GPIO15CFG 0xf14f
+#define STB0899_GPIO16CFG 0xf150
+#define STB0899_GPIO17CFG 0xf151
+#define STB0899_GPIO18CFG 0xf152
+#define STB0899_GPIO19CFG 0xf153
+#define STB0899_GPIO20CFG 0xf154
+
+#define STB0899_SDATCFG 0xf155
+#define STB0899_SCLTCFG 0xf156
+#define STB0899_AGCRFCFG 0xf157
+#define STB0899_GPIO22 0xf158 /* AGCBB2CFG */
+#define STB0899_GPIO21 0xf159 /* AGCBB1CFG */
+#define STB0899_DIRCLKCFG 0xf15a
+#define STB0899_CLKOUT27CFG 0xf15b
+#define STB0899_STDBYCFG 0xf15c
+#define STB0899_CS0CFG 0xf15d
+#define STB0899_CS1CFG 0xf15e
+#define STB0899_DISEQCOCFG 0xf15f
+
+#define STB0899_GPIO32CFG 0xf160
+#define STB0899_GPIO33CFG 0xf161
+#define STB0899_GPIO34CFG 0xf162
+#define STB0899_GPIO35CFG 0xf163
+#define STB0899_GPIO36CFG 0xf164
+#define STB0899_GPIO37CFG 0xf165
+#define STB0899_GPIO38CFG 0xf166
+#define STB0899_GPIO39CFG 0xf167
+
+#define STB0899_IRQSTATUS_3 0xf120
+#define STB0899_IRQSTATUS_2 0xf121
+#define STB0899_IRQSTATUS_1 0xf122
+#define STB0899_IRQSTATUS_0 0xf123
+
+#define STB0899_IRQMSK_3 0xf124
+#define STB0899_IRQMSK_2 0xf125
+#define STB0899_IRQMSK_1 0xf126
+#define STB0899_IRQMSK_0 0xf127
+
+#define STB0899_IRQCFG 0xf128
+
+#define STB0899_GHOSTREG 0xf000
+
+#define STB0899_S2DEMOD 0xf3fc
+#define STB0899_S2FEC 0xfafc
+
+
+#endif
diff --git a/drivers/media/dvb/frontends/stb6100.c b/drivers/media/dvb/frontends/stb6100.c
new file mode 100644
index 000000000000..ff39275ab49c
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb6100.c
@@ -0,0 +1,545 @@
+/*
+ STB6100 Silicon Tuner
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include "dvb_frontend.h"
+#include "stb6100.h"
+
+static unsigned int verbose;
+module_param(verbose, int, 0644);
+
+
+#define FE_ERROR 0
+#define FE_NOTICE 1
+#define FE_INFO 2
+#define FE_DEBUG 3
+
+#define dprintk(x, y, z, format, arg...) do { \
+ if (z) { \
+ if ((x > FE_ERROR) && (x > y)) \
+ printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \
+ else if ((x > FE_NOTICE) && (x > y)) \
+ printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \
+ else if ((x > FE_INFO) && (x > y)) \
+ printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \
+ else if ((x > FE_DEBUG) && (x > y)) \
+ printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \
+ } else { \
+ if (x > y) \
+ printk(format, ##arg); \
+ } \
+} while(0)
+
+struct stb6100_lkup {
+ u32 val_low;
+ u32 val_high;
+ u8 reg;
+};
+
+static int stb6100_release(struct dvb_frontend *fe);
+
+static const struct stb6100_lkup lkup[] = {
+ { 0, 950000, 0x0a },
+ { 950000, 1000000, 0x0a },
+ { 1000000, 1075000, 0x0c },
+ { 1075000, 1200000, 0x00 },
+ { 1200000, 1300000, 0x01 },
+ { 1300000, 1370000, 0x02 },
+ { 1370000, 1470000, 0x04 },
+ { 1470000, 1530000, 0x05 },
+ { 1530000, 1650000, 0x06 },
+ { 1650000, 1800000, 0x08 },
+ { 1800000, 1950000, 0x0a },
+ { 1950000, 2150000, 0x0c },
+ { 2150000, 9999999, 0x0c },
+ { 0, 0, 0x00 }
+};
+
+/* Register names for easy debugging. */
+static const char *stb6100_regnames[] = {
+ [STB6100_LD] = "LD",
+ [STB6100_VCO] = "VCO",
+ [STB6100_NI] = "NI",
+ [STB6100_NF_LSB] = "NF",
+ [STB6100_K] = "K",
+ [STB6100_G] = "G",
+ [STB6100_F] = "F",
+ [STB6100_DLB] = "DLB",
+ [STB6100_TEST1] = "TEST1",
+ [STB6100_FCCK] = "FCCK",
+ [STB6100_LPEN] = "LPEN",
+ [STB6100_TEST3] = "TEST3",
+};
+
+/* Template for normalisation, i.e. setting unused or undocumented
+ * bits as required according to the documentation.
+ */
+struct stb6100_regmask {
+ u8 mask;
+ u8 set;
+};
+
+static const struct stb6100_regmask stb6100_template[] = {
+ [STB6100_LD] = { 0xff, 0x00 },
+ [STB6100_VCO] = { 0xff, 0x00 },
+ [STB6100_NI] = { 0xff, 0x00 },
+ [STB6100_NF_LSB] = { 0xff, 0x00 },
+ [STB6100_K] = { 0xc7, 0x38 },
+ [STB6100_G] = { 0xef, 0x10 },
+ [STB6100_F] = { 0x1f, 0xc0 },
+ [STB6100_DLB] = { 0x38, 0xc4 },
+ [STB6100_TEST1] = { 0x00, 0x8f },
+ [STB6100_FCCK] = { 0x40, 0x0d },
+ [STB6100_LPEN] = { 0xf0, 0x0b },
+ [STB6100_TEST3] = { 0x00, 0xde },
+};
+
+static void stb6100_normalise_regs(u8 regs[])
+{
+ int i;
+
+ for (i = 0; i < STB6100_NUMREGS; i++)
+ regs[i] = (regs[i] & stb6100_template[i].mask) | stb6100_template[i].set;
+}
+
+static int stb6100_read_regs(struct stb6100_state *state, u8 regs[])
+{
+ int rc;
+ struct i2c_msg msg = {
+ .addr = state->config->tuner_address,
+ .flags = I2C_M_RD,
+ .buf = regs,
+ .len = STB6100_NUMREGS
+ };
+
+ rc = i2c_transfer(state->i2c, &msg, 1);
+ if (unlikely(rc != 1)) {
+ dprintk(verbose, FE_ERROR, 1, "Read (0x%x) err, rc=[%d]",
+ state->config->tuner_address, rc);
+
+ return -EREMOTEIO;
+ }
+ if (unlikely(verbose > FE_DEBUG)) {
+ int i;
+
+ dprintk(verbose, FE_DEBUG, 1, " Read from 0x%02x", state->config->tuner_address);
+ for (i = 0; i < STB6100_NUMREGS; i++)
+ dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[i], regs[i]);
+ }
+ return 0;
+}
+
+static int stb6100_read_reg(struct stb6100_state *state, u8 reg)
+{
+ u8 regs[STB6100_NUMREGS];
+ int rc;
+
+ if (unlikely(reg >= STB6100_NUMREGS)) {
+ dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg);
+ return -EINVAL;
+ }
+ if ((rc = stb6100_read_regs(state, regs)) < 0)
+ return rc;
+ return (unsigned int)regs[reg];
+}
+
+static int stb6100_write_reg_range(struct stb6100_state *state, u8 buf[], int start, int len)
+{
+ int rc;
+ u8 cmdbuf[len + 1];
+ struct i2c_msg msg = {
+ .addr = state->config->tuner_address,
+ .flags = 0,
+ .buf = cmdbuf,
+ .len = len + 1
+ };
+
+ if (unlikely(start < 1 || start + len > STB6100_NUMREGS)) {
+ dprintk(verbose, FE_ERROR, 1, "Invalid register range %d:%d",
+ start, len);
+ return -EINVAL;
+ }
+ memcpy(&cmdbuf[1], buf, len);
+ cmdbuf[0] = start;
+
+ if (unlikely(verbose > FE_DEBUG)) {
+ int i;
+
+ dprintk(verbose, FE_DEBUG, 1, " Write @ 0x%02x: [%d:%d]", state->config->tuner_address, start, len);
+ for (i = 0; i < len; i++)
+ dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[start + i], buf[i]);
+ }
+ rc = i2c_transfer(state->i2c, &msg, 1);
+ if (unlikely(rc != 1)) {
+ dprintk(verbose, FE_ERROR, 1, "(0x%x) write err [%d:%d], rc=[%d]",
+ (unsigned int)state->config->tuner_address, start, len, rc);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int stb6100_write_reg(struct stb6100_state *state, u8 reg, u8 data)
+{
+ if (unlikely(reg >= STB6100_NUMREGS)) {
+ dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg);
+ return -EREMOTEIO;
+ }
+ data = (data & stb6100_template[reg].mask) | stb6100_template[reg].set;
+ return stb6100_write_reg_range(state, &data, reg, 1);
+}
+
+static int stb6100_write_regs(struct stb6100_state *state, u8 regs[])
+{
+ stb6100_normalise_regs(regs);
+ return stb6100_write_reg_range(state, &regs[1], 1, STB6100_NUMREGS - 1);
+}
+
+static int stb6100_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ int rc;
+ struct stb6100_state *state = fe->tuner_priv;
+
+ if ((rc = stb6100_read_reg(state, STB6100_LD)) < 0)
+ return rc;
+
+ return (rc & STB6100_LD_LOCK) ? TUNER_STATUS_LOCKED : 0;
+}
+
+static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ int rc;
+ u8 f;
+ struct stb6100_state *state = fe->tuner_priv;
+
+ if ((rc = stb6100_read_reg(state, STB6100_F)) < 0)
+ return rc;
+ f = rc & STB6100_F_F;
+
+ state->status.bandwidth = (f + 5) * 2000; /* x2 for ZIF */
+
+ *bandwidth = state->bandwidth = state->status.bandwidth * 1000;
+ dprintk(verbose, FE_DEBUG, 1, "bandwidth = %u Hz", state->bandwidth);
+ return 0;
+}
+
+static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
+{
+ u32 tmp;
+ int rc;
+ struct stb6100_state *state = fe->tuner_priv;
+
+ dprintk(verbose, FE_DEBUG, 1, "set bandwidth to %u Hz", bandwidth);
+
+ bandwidth /= 2; /* ZIF */
+
+ if (bandwidth >= 36000000) /* F[4:0] BW/2 max =31+5=36 mhz for F=31 */
+ tmp = 31;
+ else if (bandwidth <= 5000000) /* bw/2 min = 5Mhz for F=0 */
+ tmp = 0;
+ else /* if 5 < bw/2 < 36 */
+ tmp = (bandwidth + 500000) / 1000000 - 5;
+
+ /* Turn on LPF bandwidth setting clock control,
+ * set bandwidth, wait 10ms, turn off.
+ */
+ if ((rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d | STB6100_FCCK_FCCK)) < 0)
+ return rc;
+ if ((rc = stb6100_write_reg(state, STB6100_F, 0xc0 | tmp)) < 0)
+ return rc;
+ msleep(1);
+ if ((rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d)) < 0)
+ return rc;
+
+ return 0;
+}
+
+static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ int rc;
+ u32 nint, nfrac, fvco;
+ int psd2, odiv;
+ struct stb6100_state *state = fe->tuner_priv;
+ u8 regs[STB6100_NUMREGS];
+
+ if ((rc = stb6100_read_regs(state, regs)) < 0)
+ return rc;
+
+ odiv = (regs[STB6100_VCO] & STB6100_VCO_ODIV) >> STB6100_VCO_ODIV_SHIFT;
+ psd2 = (regs[STB6100_K] & STB6100_K_PSD2) >> STB6100_K_PSD2_SHIFT;
+ nint = regs[STB6100_NI];
+ nfrac = ((regs[STB6100_K] & STB6100_K_NF_MSB) << 8) | regs[STB6100_NF_LSB];
+ fvco = (nfrac * state->reference >> (9 - psd2)) + (nint * state->reference << psd2);
+ *frequency = state->frequency = fvco >> (odiv + 1);
+
+ dprintk(verbose, FE_DEBUG, 1,
+ "frequency = %u kHz, odiv = %u, psd2 = %u, fxtal = %u kHz, fvco = %u kHz, N(I) = %u, N(F) = %u",
+ state->frequency, odiv, psd2, state->reference, fvco, nint, nfrac);
+ return 0;
+}
+
+
+static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency)
+{
+ int rc;
+ const struct stb6100_lkup *ptr;
+ struct stb6100_state *state = fe->tuner_priv;
+ struct dvb_frontend_parameters p;
+
+ u32 srate = 0, fvco, nint, nfrac;
+ u8 regs[STB6100_NUMREGS];
+ u8 g, psd2, odiv;
+
+ if ((rc = stb6100_read_regs(state, regs)) < 0)
+ return rc;
+
+ if (fe->ops.get_frontend) {
+ dprintk(verbose, FE_DEBUG, 1, "Get frontend parameters");
+ fe->ops.get_frontend(fe, &p);
+ }
+ srate = p.u.qpsk.symbol_rate;
+
+ regs[STB6100_DLB] = 0xdc;
+ /* Disable LPEN */
+ regs[STB6100_LPEN] &= ~STB6100_LPEN_LPEN; /* PLL Loop disabled */
+
+ if ((rc = stb6100_write_regs(state, regs)) < 0)
+ return rc;
+
+ /* Baseband gain. */
+ if (srate >= 15000000)
+ g = 9; // +4 dB
+ else if (srate >= 5000000)
+ g = 11; // +8 dB
+ else
+ g = 14; // +14 dB
+
+ regs[STB6100_G] = (regs[STB6100_G] & ~STB6100_G_G) | g;
+ regs[STB6100_G] &= ~STB6100_G_GCT; /* mask GCT */
+ regs[STB6100_G] |= (1 << 5); /* 2Vp-p Mode */
+
+ /* VCO divide ratio (LO divide ratio, VCO prescaler enable). */
+ if (frequency <= 1075000)
+ odiv = 1;
+ else
+ odiv = 0;
+ regs[STB6100_VCO] = (regs[STB6100_VCO] & ~STB6100_VCO_ODIV) | (odiv << STB6100_VCO_ODIV_SHIFT);
+
+ if ((frequency > 1075000) && (frequency <= 1325000))
+ psd2 = 0;
+ else
+ psd2 = 1;
+ regs[STB6100_K] = (regs[STB6100_K] & ~STB6100_K_PSD2) | (psd2 << STB6100_K_PSD2_SHIFT);
+
+ /* OSM */
+ for (ptr = lkup;
+ (ptr->val_high != 0) && !CHKRANGE(frequency, ptr->val_low, ptr->val_high);
+ ptr++);
+ if (ptr->val_high == 0) {
+ printk(KERN_ERR "%s: frequency out of range: %u kHz\n", __func__, frequency);
+ return -EINVAL;
+ }
+ regs[STB6100_VCO] = (regs[STB6100_VCO] & ~STB6100_VCO_OSM) | ptr->reg;
+
+ /* F(VCO) = F(LO) * (ODIV == 0 ? 2 : 4) */
+ fvco = frequency << (1 + odiv);
+ /* N(I) = floor(f(VCO) / (f(XTAL) * (PSD2 ? 2 : 1))) */
+ nint = fvco / (state->reference << psd2);
+ /* N(F) = round(f(VCO) / f(XTAL) * (PSD2 ? 2 : 1) - N(I)) * 2 ^ 9 */
+ nfrac = (((fvco - (nint * state->reference << psd2)) << (9 - psd2)) + state->reference / 2) / state->reference;
+ dprintk(verbose, FE_DEBUG, 1,
+ "frequency = %u, srate = %u, g = %u, odiv = %u, psd2 = %u, fxtal = %u, osm = %u, fvco = %u, N(I) = %u, N(F) = %u",
+ frequency, srate, (unsigned int)g, (unsigned int)odiv,
+ (unsigned int)psd2, state->reference,
+ ptr->reg, fvco, nint, nfrac);
+ regs[STB6100_NI] = nint;
+ regs[STB6100_NF_LSB] = nfrac;
+ regs[STB6100_K] = (regs[STB6100_K] & ~STB6100_K_NF_MSB) | ((nfrac >> 8) & STB6100_K_NF_MSB);
+ regs[STB6100_VCO] |= STB6100_VCO_OSCH; /* VCO search enabled */
+ regs[STB6100_VCO] |= STB6100_VCO_OCK; /* VCO search clock off */
+ regs[STB6100_FCCK] |= STB6100_FCCK_FCCK; /* LPF BW setting clock enabled */
+ regs[STB6100_LPEN] &= ~STB6100_LPEN_LPEN; /* PLL loop disabled */
+ /* Power up. */
+ regs[STB6100_LPEN] |= STB6100_LPEN_SYNP | STB6100_LPEN_OSCP | STB6100_LPEN_BEN;
+
+ msleep(2);
+ if ((rc = stb6100_write_regs(state, regs)) < 0)
+ return rc;
+
+ msleep(2);
+ regs[STB6100_LPEN] |= STB6100_LPEN_LPEN; /* PLL loop enabled */
+ if ((rc = stb6100_write_reg(state, STB6100_LPEN, regs[STB6100_LPEN])) < 0)
+ return rc;
+
+ regs[STB6100_VCO] &= ~STB6100_VCO_OCK; /* VCO fast search */
+ if ((rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO])) < 0)
+ return rc;
+
+ msleep(10); /* wait for LO to lock */
+ regs[STB6100_VCO] &= ~STB6100_VCO_OSCH; /* vco search disabled */
+ regs[STB6100_VCO] |= STB6100_VCO_OCK; /* search clock off */
+ if ((rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO])) < 0)
+ return rc;
+ regs[STB6100_FCCK] &= ~STB6100_FCCK_FCCK; /* LPF BW clock disabled */
+ stb6100_normalise_regs(regs);
+ if ((rc = stb6100_write_reg_range(state, &regs[1], 1, STB6100_NUMREGS - 3)) < 0)
+ return rc;
+
+ msleep(100);
+
+ return 0;
+}
+
+static int stb6100_sleep(struct dvb_frontend *fe)
+{
+ /* TODO: power down */
+ return 0;
+}
+
+static int stb6100_init(struct dvb_frontend *fe)
+{
+ struct stb6100_state *state = fe->tuner_priv;
+ struct tuner_state *status = &state->status;
+
+ status->tunerstep = 125000;
+ status->ifreq = 0;
+ status->refclock = 27000000; /* Hz */
+ status->iqsense = 1;
+ status->bandwidth = 36000; /* kHz */
+ state->bandwidth = status->bandwidth * 1000; /* MHz */
+ state->reference = status->refclock / 1000; /* kHz */
+
+ /* Set default bandwidth. */
+ return stb6100_set_bandwidth(fe, status->bandwidth);
+}
+
+static int stb6100_get_state(struct dvb_frontend *fe,
+ enum tuner_param param,
+ struct tuner_state *state)
+{
+ switch (param) {
+ case DVBFE_TUNER_FREQUENCY:
+ stb6100_get_frequency(fe, &state->frequency);
+ break;
+ case DVBFE_TUNER_TUNERSTEP:
+ break;
+ case DVBFE_TUNER_IFFREQ:
+ break;
+ case DVBFE_TUNER_BANDWIDTH:
+ stb6100_get_bandwidth(fe, &state->bandwidth);
+ break;
+ case DVBFE_TUNER_REFCLOCK:
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int stb6100_set_state(struct dvb_frontend *fe,
+ enum tuner_param param,
+ struct tuner_state *state)
+{
+ struct stb6100_state *tstate = fe->tuner_priv;
+
+ switch (param) {
+ case DVBFE_TUNER_FREQUENCY:
+ stb6100_set_frequency(fe, state->frequency);
+ tstate->frequency = state->frequency;
+ break;
+ case DVBFE_TUNER_TUNERSTEP:
+ break;
+ case DVBFE_TUNER_IFFREQ:
+ break;
+ case DVBFE_TUNER_BANDWIDTH:
+ stb6100_set_bandwidth(fe, state->bandwidth);
+ tstate->bandwidth = state->bandwidth;
+ break;
+ case DVBFE_TUNER_REFCLOCK:
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static struct dvb_tuner_ops stb6100_ops = {
+ .info = {
+ .name = "STB6100 Silicon Tuner",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_step = 0,
+ },
+
+ .init = stb6100_init,
+ .sleep = stb6100_sleep,
+ .get_status = stb6100_get_status,
+ .get_state = stb6100_get_state,
+ .set_state = stb6100_set_state,
+ .release = stb6100_release
+};
+
+struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe,
+ struct stb6100_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct stb6100_state *state = NULL;
+
+ state = kzalloc(sizeof (struct stb6100_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ state->config = config;
+ state->i2c = i2c;
+ state->frontend = fe;
+ state->reference = config->refclock / 1000; /* kHz */
+ fe->tuner_priv = state;
+ fe->ops.tuner_ops = stb6100_ops;
+
+ printk("%s: Attaching STB6100 \n", __func__);
+ return fe;
+
+error:
+ kfree(state);
+ return NULL;
+}
+
+static int stb6100_release(struct dvb_frontend *fe)
+{
+ struct stb6100_state *state = fe->tuner_priv;
+
+ fe->tuner_priv = NULL;
+ kfree(state);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(stb6100_attach);
+MODULE_PARM_DESC(verbose, "Set Verbosity level");
+
+MODULE_AUTHOR("Manu Abraham");
+MODULE_DESCRIPTION("STB6100 Silicon tuner");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/stb6100.h b/drivers/media/dvb/frontends/stb6100.h
new file mode 100644
index 000000000000..395d056599a6
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb6100.h
@@ -0,0 +1,115 @@
+/*
+ STB6100 Silicon Tuner
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ 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 __STB_6100_REG_H
+#define __STB_6100_REG_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+#define STB6100_LD 0x00
+#define STB6100_LD_LOCK (1 << 0)
+
+#define STB6100_VCO 0x01
+#define STB6100_VCO_OSCH (0x01 << 7)
+#define STB6100_VCO_OSCH_SHIFT 7
+#define STB6100_VCO_OCK (0x03 << 5)
+#define STB6100_VCO_OCK_SHIFT 5
+#define STB6100_VCO_ODIV (0x01 << 4)
+#define STB6100_VCO_ODIV_SHIFT 4
+#define STB6100_VCO_OSM (0x0f << 0)
+
+#define STB6100_NI 0x02
+#define STB6100_NF_LSB 0x03
+
+#define STB6100_K 0x04
+#define STB6100_K_PSD2 (0x01 << 2)
+#define STB6100_K_PSD2_SHIFT 2
+#define STB6100_K_NF_MSB (0x03 << 0)
+
+#define STB6100_G 0x05
+#define STB6100_G_G (0x0f << 0)
+#define STB6100_G_GCT (0x07 << 5)
+
+#define STB6100_F 0x06
+#define STB6100_F_F (0x1f << 0)
+
+#define STB6100_DLB 0x07
+
+#define STB6100_TEST1 0x08
+
+#define STB6100_FCCK 0x09
+#define STB6100_FCCK_FCCK (0x01 << 6)
+
+#define STB6100_LPEN 0x0a
+#define STB6100_LPEN_LPEN (0x01 << 4)
+#define STB6100_LPEN_SYNP (0x01 << 5)
+#define STB6100_LPEN_OSCP (0x01 << 6)
+#define STB6100_LPEN_BEN (0x01 << 7)
+
+#define STB6100_TEST3 0x0b
+
+#define STB6100_NUMREGS 0x0c
+
+
+#define INRANGE(val, x, y) (((x <= val) && (val <= y)) || \
+ ((y <= val) && (val <= x)) ? 1 : 0)
+
+#define CHKRANGE(val, x, y) (((val >= x) && (val < y)) ? 1 : 0)
+
+struct stb6100_config {
+ u8 tuner_address;
+ u32 refclock;
+};
+
+struct stb6100_state {
+ struct i2c_adapter *i2c;
+
+ const struct stb6100_config *config;
+ struct dvb_tuner_ops ops;
+ struct dvb_frontend *frontend;
+ struct tuner_state status;
+
+ u32 frequency;
+ u32 srate;
+ u32 bandwidth;
+ u32 reference;
+};
+
+#if defined(CONFIG_DVB_STB6100) || (defined(CONFIG_DVB_STB6100_MODULE) && defined(MODULE))
+
+extern struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe,
+ struct stb6100_config *config,
+ struct i2c_adapter *i2c);
+
+#else
+
+static inline struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe,
+ struct stb6100_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+#endif //CONFIG_DVB_STB6100
+
+#endif
diff --git a/drivers/media/dvb/frontends/stb6100_cfg.h b/drivers/media/dvb/frontends/stb6100_cfg.h
new file mode 100644
index 000000000000..d3133405dc03
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb6100_cfg.h
@@ -0,0 +1,108 @@
+/*
+ STB6100 Silicon Tuner
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ 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.
+*/
+
+static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state t_state;
+ int err = 0;
+
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->get_state) {
+ if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ *frequency = t_state.frequency;
+ printk("%s: Frequency=%d\n", __func__, t_state.frequency);
+ }
+ return 0;
+}
+
+static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state t_state;
+ int err = 0;
+
+ t_state.frequency = frequency;
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->set_state) {
+ if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ }
+ printk("%s: Frequency=%d\n", __func__, t_state.frequency);
+ return 0;
+}
+
+static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct dvb_frontend_ops *frontend_ops = &fe->ops;
+ struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
+ struct tuner_state t_state;
+ int err = 0;
+
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->get_state) {
+ if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ *bandwidth = t_state.bandwidth;
+ }
+ printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth);
+ return 0;
+}
+
+static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state t_state;
+ int err = 0;
+
+ t_state.bandwidth = bandwidth;
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->set_state) {
+ if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ }
+ printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth);
+ return 0;
+}
diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c
new file mode 100644
index 000000000000..b6d177799104
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda8261.c
@@ -0,0 +1,230 @@
+/*
+ TDA8261 8PSK/QPSK tuner driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "dvb_frontend.h"
+#include "tda8261.h"
+
+struct tda8261_state {
+ struct dvb_frontend *fe;
+ struct i2c_adapter *i2c;
+ const struct tda8261_config *config;
+
+ /* state cache */
+ u32 frequency;
+ u32 bandwidth;
+};
+
+static int tda8261_read(struct tda8261_state *state, u8 *buf)
+{
+ const struct tda8261_config *config = state->config;
+ int err = 0;
+ struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 2 };
+
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1)
+ printk("%s: read error, err=%d\n", __func__, err);
+
+ return err;
+}
+
+static int tda8261_write(struct tda8261_state *state, u8 *buf)
+{
+ const struct tda8261_config *config = state->config;
+ int err = 0;
+ struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = 4 };
+
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1)
+ printk("%s: write error, err=%d\n", __func__, err);
+
+ return err;
+}
+
+static int tda8261_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct tda8261_state *state = fe->tuner_priv;
+ u8 result = 0;
+ int err = 0;
+
+ *status = 0;
+
+ if ((err = tda8261_read(state, &result)) < 0) {
+ printk("%s: I/O Error\n", __func__);
+ return err;
+ }
+ if ((result >> 6) & 0x01) {
+ printk("%s: Tuner Phase Locked\n", __func__);
+ *status = 1;
+ }
+
+ return err;
+}
+
+static const u32 div_tab[] = { 2000, 1000, 500, 250, 125 }; /* kHz */
+static const u8 ref_div[] = { 0x00, 0x01, 0x02, 0x05, 0x07 };
+
+static int tda8261_get_state(struct dvb_frontend *fe,
+ enum tuner_param param,
+ struct tuner_state *tstate)
+{
+ struct tda8261_state *state = fe->tuner_priv;
+ int err = 0;
+
+ switch (param) {
+ case DVBFE_TUNER_FREQUENCY:
+ tstate->frequency = state->frequency;
+ break;
+ case DVBFE_TUNER_BANDWIDTH:
+ tstate->bandwidth = 40000000; /* FIXME! need to calculate Bandwidth */
+ break;
+ default:
+ printk("%s: Unknown parameter (param=%d)\n", __func__, param);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static int tda8261_set_state(struct dvb_frontend *fe,
+ enum tuner_param param,
+ struct tuner_state *tstate)
+{
+ struct tda8261_state *state = fe->tuner_priv;
+ const struct tda8261_config *config = state->config;
+ u32 frequency, N, status = 0;
+ u8 buf[4];
+ int err = 0;
+
+ if (param & DVBFE_TUNER_FREQUENCY) {
+ /**
+ * N = Max VCO Frequency / Channel Spacing
+ * Max VCO Frequency = VCO frequency + (channel spacing - 1)
+ * (to account for half channel spacing on either side)
+ */
+ frequency = tstate->frequency;
+ if ((frequency < 950000) || (frequency > 2150000)) {
+ printk("%s: Frequency beyond limits, frequency=%d\n", __func__, frequency);
+ return -EINVAL;
+ }
+ N = (frequency + (div_tab[config->step_size] - 1)) / div_tab[config->step_size];
+ printk("%s: Step size=%d, Divider=%d, PG=0x%02x (%d)\n",
+ __func__, config->step_size, div_tab[config->step_size], N, N);
+
+ buf[0] = (N >> 8) & 0xff;
+ buf[1] = N & 0xff;
+ buf[2] = (0x01 << 7) | ((ref_div[config->step_size] & 0x07) << 1);
+
+ if (frequency < 1450000)
+ buf[3] = 0x00;
+ if (frequency < 2000000)
+ buf[3] = 0x40;
+ if (frequency < 2150000)
+ buf[3] = 0x80;
+
+ /* Set params */
+ if ((err = tda8261_write(state, buf)) < 0) {
+ printk("%s: I/O Error\n", __func__);
+ return err;
+ }
+ /* sleep for some time */
+ printk("%s: Waiting to Phase LOCK\n", __func__);
+ msleep(20);
+ /* check status */
+ if ((err = tda8261_get_status(fe, &status)) < 0) {
+ printk("%s: I/O Error\n", __func__);
+ return err;
+ }
+ if (status == 1) {
+ printk("%s: Tuner Phase locked: status=%d\n", __func__, status);
+ state->frequency = frequency; /* cache successful state */
+ } else {
+ printk("%s: No Phase lock: status=%d\n", __func__, status);
+ }
+ } else {
+ printk("%s: Unknown parameter (param=%d)\n", __func__, param);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tda8261_release(struct dvb_frontend *fe)
+{
+ struct tda8261_state *state = fe->tuner_priv;
+
+ fe->tuner_priv = NULL;
+ kfree(state);
+ return 0;
+}
+
+static struct dvb_tuner_ops tda8261_ops = {
+
+ .info = {
+ .name = "TDA8261",
+// .tuner_name = NULL,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_step = 0
+ },
+
+ .set_state = tda8261_set_state,
+ .get_state = tda8261_get_state,
+ .get_status = tda8261_get_status,
+ .release = tda8261_release
+};
+
+struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe,
+ const struct tda8261_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct tda8261_state *state = NULL;
+
+ if ((state = kzalloc(sizeof (struct tda8261_state), GFP_KERNEL)) == NULL)
+ goto exit;
+
+ state->config = config;
+ state->i2c = i2c;
+ state->fe = fe;
+ fe->tuner_priv = state;
+ fe->ops.tuner_ops = tda8261_ops;
+
+ fe->ops.tuner_ops.info.frequency_step = div_tab[config->step_size];
+// fe->ops.tuner_ops.tuner_name = &config->buf;
+
+// printk("%s: Attaching %s TDA8261 8PSK/QPSK tuner\n",
+// __func__, fe->ops.tuner_ops.tuner_name);
+ printk("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__);
+
+ return fe;
+
+exit:
+ kfree(state);
+ return NULL;
+}
+
+EXPORT_SYMBOL(tda8261_attach);
+MODULE_PARM_DESC(verbose, "Set verbosity level");
+
+MODULE_AUTHOR("Manu Abraham");
+MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tda8261.h b/drivers/media/dvb/frontends/tda8261.h
new file mode 100644
index 000000000000..006e45351b94
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda8261.h
@@ -0,0 +1,55 @@
+/*
+ TDA8261 8PSK/QPSK tuner driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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 __TDA8261_H
+#define __TDA8261_H
+
+enum tda8261_step {
+ TDA8261_STEP_2000 = 0, /* 2000 kHz */
+ TDA8261_STEP_1000, /* 1000 kHz */
+ TDA8261_STEP_500, /* 500 kHz */
+ TDA8261_STEP_250, /* 250 kHz */
+ TDA8261_STEP_125 /* 125 kHz */
+};
+
+struct tda8261_config {
+// u8 buf[16];
+ u8 addr;
+ enum tda8261_step step_size;
+};
+
+#if defined(CONFIG_DVB_TDA8261) || (defined(CONFIG_DVB_TDA8261_MODULE) && defined(MODULE))
+
+extern struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe,
+ const struct tda8261_config *config,
+ struct i2c_adapter *i2c);
+
+#else
+
+static inline struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe,
+ const struct tda8261_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+#endif //CONFIG_DVB_TDA8261
+
+#endif// __TDA8261_H
diff --git a/drivers/media/dvb/frontends/tda8261_cfg.h b/drivers/media/dvb/frontends/tda8261_cfg.h
new file mode 100644
index 000000000000..1af1ee49b542
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda8261_cfg.h
@@ -0,0 +1,84 @@
+/*
+ TDA8261 8PSK/QPSK tuner driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+*/
+
+static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state t_state;
+ int err = 0;
+
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->get_state) {
+ if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ *frequency = t_state.frequency;
+ printk("%s: Frequency=%d\n", __func__, t_state.frequency);
+ }
+ return 0;
+}
+
+static int tda8261_set_frequency(struct dvb_frontend *fe, u32 frequency)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state t_state;
+ int err = 0;
+
+ t_state.frequency = frequency;
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->set_state) {
+ if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ }
+ printk("%s: Frequency=%d\n", __func__, t_state.frequency);
+ return 0;
+}
+
+static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct dvb_frontend_ops *frontend_ops = &fe->ops;
+ struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
+ struct tuner_state t_state;
+ int err = 0;
+
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->get_state) {
+ if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ *bandwidth = t_state.bandwidth;
+ }
+ printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth);
+ return 0;
+}
diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c
index 36a5a1c101d5..5506f80e180e 100644
--- a/drivers/media/dvb/frontends/zl10353.c
+++ b/drivers/media/dvb/frontends/zl10353.c
@@ -220,15 +220,18 @@ static int zl10353_set_parameters(struct dvb_frontend *fe,
/* These are extrapolated from the 7 and 8MHz values */
zl10353_single_write(fe, MCLK_RATIO, 0x97);
zl10353_single_write(fe, 0x64, 0x34);
+ zl10353_single_write(fe, 0xcc, 0xdd);
break;
case BANDWIDTH_7_MHZ:
zl10353_single_write(fe, MCLK_RATIO, 0x86);
zl10353_single_write(fe, 0x64, 0x35);
+ zl10353_single_write(fe, 0xcc, 0x73);
break;
case BANDWIDTH_8_MHZ:
default:
zl10353_single_write(fe, MCLK_RATIO, 0x75);
zl10353_single_write(fe, 0x64, 0x36);
+ zl10353_single_write(fe, 0xcc, 0x73);
}
zl10353_calc_nominal_rate(fe, op->bandwidth, &nominal_rate);
diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c
index e98d6caf2c23..fd62e0b85621 100644
--- a/drivers/media/dvb/siano/sms-cards.c
+++ b/drivers/media/dvb/siano/sms-cards.c
@@ -38,6 +38,16 @@ struct usb_device_id smsusb_id_table[] = {
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A },
{ USB_DEVICE(0x2040, 0x1801),
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B },
+ { USB_DEVICE(0x2040, 0x2000),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+ { USB_DEVICE(0x2040, 0x2009),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 },
+ { USB_DEVICE(0x2040, 0x200a),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+ { USB_DEVICE(0x2040, 0x2010),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+ { USB_DEVICE(0x2040, 0x2019),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
{ USB_DEVICE(0x2040, 0x5500),
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
{ USB_DEVICE(0x2040, 0x5510),
@@ -96,6 +106,21 @@ static struct sms_board sms_boards[] = {
.name = "Hauppauge WinTV MiniStick",
.type = SMS_NOVA_B0,
.fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw",
+ .led_power = 26,
+ .led_lo = 27,
+ .led_hi = 28,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD] = {
+ .name = "Hauppauge WinTV MiniCard",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw",
+ .lna_ctrl = 29,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2] = {
+ .name = "Hauppauge WinTV MiniCard",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw",
+ .lna_ctrl = 1,
},
};
@@ -106,3 +131,88 @@ struct sms_board *sms_get_board(int id)
return &sms_boards[id];
}
+static int sms_set_gpio(struct smscore_device_t *coredev, u32 pin, int enable)
+{
+ int ret;
+ struct smscore_gpio_config gpioconfig = {
+ .direction = SMS_GPIO_DIRECTION_OUTPUT,
+ .pullupdown = SMS_GPIO_PULLUPDOWN_NONE,
+ .inputcharacteristics = SMS_GPIO_INPUTCHARACTERISTICS_NORMAL,
+ .outputslewrate = SMS_GPIO_OUTPUTSLEWRATE_FAST,
+ .outputdriving = SMS_GPIO_OUTPUTDRIVING_4mA,
+ };
+
+ if (pin == 0)
+ return -EINVAL;
+
+ ret = smscore_configure_gpio(coredev, pin, &gpioconfig);
+
+ if (ret < 0)
+ return ret;
+
+ return smscore_set_gpio(coredev, pin, enable);
+}
+
+int sms_board_setup(struct smscore_device_t *coredev)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ /* turn off all LEDs */
+ sms_set_gpio(coredev, board->led_power, 0);
+ sms_set_gpio(coredev, board->led_hi, 0);
+ sms_set_gpio(coredev, board->led_lo, 0);
+ break;
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+ /* turn off LNA */
+ sms_set_gpio(coredev, board->lna_ctrl, 0);
+ break;
+ }
+ return 0;
+}
+
+int sms_board_power(struct smscore_device_t *coredev, int onoff)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ /* power LED */
+ sms_set_gpio(coredev,
+ board->led_power, onoff ? 1 : 0);
+ break;
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+ /* LNA */
+ sms_set_gpio(coredev,
+ board->lna_ctrl, onoff ? 1 : 0);
+ break;
+ }
+ return 0;
+}
+
+int sms_board_led_feedback(struct smscore_device_t *coredev, int led)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ /* dont touch GPIO if LEDs are already set */
+ if (smscore_led_state(coredev, -1) == led)
+ return 0;
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ sms_set_gpio(coredev,
+ board->led_lo, (led & SMS_LED_LO) ? 1 : 0);
+ sms_set_gpio(coredev,
+ board->led_hi, (led & SMS_LED_HI) ? 1 : 0);
+
+ smscore_led_state(coredev, led);
+ break;
+ }
+ return 0;
+}
diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/dvb/siano/sms-cards.h
index c8f3da6f9bc1..8e0fe9fd2610 100644
--- a/drivers/media/dvb/siano/sms-cards.h
+++ b/drivers/media/dvb/siano/sms-cards.h
@@ -32,14 +32,27 @@
#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A 6
#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B 7
#define SMS1XXX_BOARD_HAUPPAUGE_WINDHAM 8
+#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD 9
+#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 10
struct sms_board {
enum sms_device_type_st type;
char *name, *fw[DEVICE_MODE_MAX];
+
+ /* gpios */
+ int led_power, led_hi, led_lo, lna_ctrl;
};
struct sms_board *sms_get_board(int id);
+int sms_board_setup(struct smscore_device_t *coredev);
+
+#define SMS_LED_OFF 0
+#define SMS_LED_LO 1
+#define SMS_LED_HI 2
+int sms_board_led_feedback(struct smscore_device_t *coredev, int led);
+int sms_board_power(struct smscore_device_t *coredev, int onoff);
+
extern struct usb_device_id smsusb_id_table[];
#endif /* __SMS_CARDS_H__ */
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c
index 6576fbb40fc6..cf613f22fb8d 100644
--- a/drivers/media/dvb/siano/smscoreapi.c
+++ b/drivers/media/dvb/siano/smscoreapi.c
@@ -91,6 +91,7 @@ struct smscore_device_t {
struct completion init_device_done, reload_start_done, resume_done;
int board_id;
+ int led_state;
};
void smscore_set_board_id(struct smscore_device_t *core, int id)
@@ -98,6 +99,13 @@ void smscore_set_board_id(struct smscore_device_t *core, int id)
core->board_id = id;
}
+int smscore_led_state(struct smscore_device_t *core, int led)
+{
+ if (led >= 0)
+ core->led_state = led;
+ return core->led_state;
+}
+
int smscore_get_board_id(struct smscore_device_t *core)
{
return core->board_id;
@@ -1187,6 +1195,76 @@ int smsclient_sendrequest(struct smscore_client_t *client,
}
+int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin,
+ struct smscore_gpio_config *pinconfig)
+{
+ struct {
+ struct SmsMsgHdr_ST hdr;
+ u32 data[6];
+ } msg;
+
+ if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
+ msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
+ msg.hdr.msgDstId = HIF_TASK;
+ msg.hdr.msgFlags = 0;
+ msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ;
+ msg.hdr.msgLength = sizeof(msg);
+
+ msg.data[0] = pin;
+ msg.data[1] = pinconfig->pullupdown;
+
+ /* Convert slew rate for Nova: Fast(0) = 3 / Slow(1) = 0; */
+ msg.data[2] = pinconfig->outputslewrate == 0 ? 3 : 0;
+
+ switch (pinconfig->outputdriving) {
+ case SMS_GPIO_OUTPUTDRIVING_16mA:
+ msg.data[3] = 7; /* Nova - 16mA */
+ break;
+ case SMS_GPIO_OUTPUTDRIVING_12mA:
+ msg.data[3] = 5; /* Nova - 11mA */
+ break;
+ case SMS_GPIO_OUTPUTDRIVING_8mA:
+ msg.data[3] = 3; /* Nova - 7mA */
+ break;
+ case SMS_GPIO_OUTPUTDRIVING_4mA:
+ default:
+ msg.data[3] = 2; /* Nova - 4mA */
+ break;
+ }
+
+ msg.data[4] = pinconfig->direction;
+ msg.data[5] = 0;
+ } else /* TODO: SMS_DEVICE_FAMILY1 */
+ return -EINVAL;
+
+ return coredev->sendrequest_handler(coredev->context,
+ &msg, sizeof(msg));
+}
+
+int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level)
+{
+ struct {
+ struct SmsMsgHdr_ST hdr;
+ u32 data[3];
+ } msg;
+
+ if (pin > MAX_GPIO_PIN_NUMBER)
+ return -EINVAL;
+
+ msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
+ msg.hdr.msgDstId = HIF_TASK;
+ msg.hdr.msgFlags = 0;
+ msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ;
+ msg.hdr.msgLength = sizeof(msg);
+
+ msg.data[0] = pin;
+ msg.data[1] = level ? 1 : 0;
+ msg.data[2] = 0;
+
+ return coredev->sendrequest_handler(coredev->context,
+ &msg, sizeof(msg));
+}
+
static int __init smscore_module_init(void)
{
int rc = 0;
diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h
index 8d973f726fb8..760e233fcbc5 100644
--- a/drivers/media/dvb/siano/smscoreapi.h
+++ b/drivers/media/dvb/siano/smscoreapi.h
@@ -186,6 +186,8 @@ struct smsclient_params_t {
#define MSG_SW_RELOAD_EXEC_REQ 704
#define MSG_SW_RELOAD_EXEC_RES 705
#define MSG_SMS_SPI_INT_LINE_SET_REQ 710
+#define MSG_SMS_GPIO_CONFIG_EX_REQ 712
+#define MSG_SMS_GPIO_CONFIG_EX_RES 713
#define MSG_SMS_ISDBT_TUNE_REQ 776
#define MSG_SMS_ISDBT_TUNE_RES 777
@@ -341,6 +343,32 @@ struct SmsMsgStatisticsInfo_ST {
};
+struct smscore_gpio_config {
+#define SMS_GPIO_DIRECTION_INPUT 0
+#define SMS_GPIO_DIRECTION_OUTPUT 1
+ u8 direction;
+
+#define SMS_GPIO_PULLUPDOWN_NONE 0
+#define SMS_GPIO_PULLUPDOWN_PULLDOWN 1
+#define SMS_GPIO_PULLUPDOWN_PULLUP 2
+#define SMS_GPIO_PULLUPDOWN_KEEPER 3
+ u8 pullupdown;
+
+#define SMS_GPIO_INPUTCHARACTERISTICS_NORMAL 0
+#define SMS_GPIO_INPUTCHARACTERISTICS_SCHMITT 1
+ u8 inputcharacteristics;
+
+#define SMS_GPIO_OUTPUTSLEWRATE_FAST 0
+#define SMS_GPIO_OUTPUTSLEWRATE_SLOW 1
+ u8 outputslewrate;
+
+#define SMS_GPIO_OUTPUTDRIVING_4mA 0
+#define SMS_GPIO_OUTPUTDRIVING_8mA 1
+#define SMS_GPIO_OUTPUTDRIVING_12mA 2
+#define SMS_GPIO_OUTPUTDRIVING_16mA 3
+ u8 outputdriving;
+};
+
struct smsdvb_client_t {
struct list_head entry;
@@ -353,7 +381,7 @@ struct smsdvb_client_t {
struct dvb_frontend frontend;
fe_status_t fe_status;
- int fe_ber, fe_snr, fe_signal_strength;
+ int fe_ber, fe_snr, fe_unc, fe_signal_strength;
struct completion tune_done, stat_done;
@@ -396,9 +424,15 @@ struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev);
extern void smscore_putbuffer(struct smscore_device_t *coredev,
struct smscore_buffer_t *cb);
+int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin,
+ struct smscore_gpio_config *pinconfig);
+int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level);
+
void smscore_set_board_id(struct smscore_device_t *core, int id);
int smscore_get_board_id(struct smscore_device_t *core);
+int smscore_led_state(struct smscore_device_t *core, int led);
+
/* smsdvb.c */
int smsdvb_register(void);
void smsdvb_unregister(void);
diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c
index 8d490e133f35..2da953a4f4f5 100644
--- a/drivers/media/dvb/siano/smsdvb.c
+++ b/drivers/media/dvb/siano/smsdvb.c
@@ -60,6 +60,7 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
client->fe_snr = p->Stat.SNR;
client->fe_ber = p->Stat.BER;
+ client->fe_unc = p->Stat.BERErrorCount;
if (p->Stat.InBandPwr < -95)
client->fe_signal_strength = 0;
@@ -72,6 +73,7 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
client->fe_status = 0;
client->fe_snr =
client->fe_ber =
+ client->fe_unc =
client->fe_signal_strength = 0;
}
@@ -165,8 +167,18 @@ static int smsdvb_send_statistics_request(struct smsdvb_client_t *client)
struct SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ,
DVBT_BDA_CONTROL_MSG_ID,
HIF_TASK, sizeof(struct SmsMsgHdr_ST), 0 };
- return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
- &client->stat_done);
+ int ret = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
+ &client->stat_done);
+ if (ret < 0)
+ return ret;
+
+ if (client->fe_status & FE_HAS_LOCK)
+ sms_board_led_feedback(client->coredev,
+ (client->fe_unc == 0) ?
+ SMS_LED_HI : SMS_LED_LO);
+ else
+ sms_board_led_feedback(client->coredev, SMS_LED_OFF);
+ return ret;
}
static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat)
@@ -217,6 +229,18 @@ static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr)
return rc;
}
+static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+ int rc = smsdvb_send_statistics_request(client);
+
+ if (!rc)
+ *ucblocks = client->fe_unc;
+
+ return rc;
+}
+
static int smsdvb_get_tune_settings(struct dvb_frontend *fe,
struct dvb_frontend_tune_settings *tune)
{
@@ -273,6 +297,28 @@ static int smsdvb_get_frontend(struct dvb_frontend *fe,
/* todo: */
memcpy(fep, &client->fe_params,
sizeof(struct dvb_frontend_parameters));
+
+ return 0;
+}
+
+static int smsdvb_init(struct dvb_frontend *fe)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+
+ sms_board_power(client->coredev, 1);
+
+ return 0;
+}
+
+static int smsdvb_sleep(struct dvb_frontend *fe)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+
+ sms_board_led_feedback(client->coredev, SMS_LED_OFF);
+ sms_board_power(client->coredev, 0);
+
return 0;
}
@@ -308,6 +354,10 @@ static struct dvb_frontend_ops smsdvb_fe_ops = {
.read_ber = smsdvb_read_ber,
.read_signal_strength = smsdvb_read_signal_strength,
.read_snr = smsdvb_read_snr,
+ .read_ucblocks = smsdvb_read_ucblocks,
+
+ .init = smsdvb_init,
+ .sleep = smsdvb_sleep,
};
static int smsdvb_hotplug(struct smscore_device_t *coredev,
@@ -402,6 +452,8 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev,
sms_info("success");
+ sms_board_setup(coredev);
+
return 0;
client_error:
diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c
index 87a3c24454b9..5d7ca3417719 100644
--- a/drivers/media/dvb/siano/smsusb.c
+++ b/drivers/media/dvb/siano/smsusb.c
@@ -432,11 +432,56 @@ static void smsusb_disconnect(struct usb_interface *intf)
smsusb_term_device(intf);
}
+static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg)
+{
+ struct smsusb_device_t *dev =
+ (struct smsusb_device_t *)usb_get_intfdata(intf);
+ printk(KERN_INFO "%s Entering status %d.\n", __func__, msg.event);
+ smsusb_stop_streaming(dev);
+ return 0;
+}
+
+static int smsusb_resume(struct usb_interface *intf)
+{
+ int rc, i;
+ struct smsusb_device_t *dev =
+ (struct smsusb_device_t *)usb_get_intfdata(intf);
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ printk(KERN_INFO "%s Entering.\n", __func__);
+ usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
+ usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)
+ printk(KERN_INFO "endpoint %d %02x %02x %d\n", i,
+ intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,
+ intf->cur_altsetting->endpoint[i].desc.bmAttributes,
+ intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
+
+ if (intf->num_altsetting > 0) {
+ rc = usb_set_interface(udev,
+ intf->cur_altsetting->desc.
+ bInterfaceNumber, 0);
+ if (rc < 0) {
+ printk(KERN_INFO
+ "%s usb_set_interface failed, rc %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ smsusb_start_streaming(dev);
+ return 0;
+}
+
static struct usb_driver smsusb_driver = {
.name = "sms1xxx",
.probe = smsusb_probe,
.disconnect = smsusb_disconnect,
.id_table = smsusb_id_table,
+
+ .suspend = smsusb_suspend,
+ .resume = smsusb_resume,
};
int smsusb_register(void)
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
index 401a04effc06..ab0bcd208c78 100644
--- a/drivers/media/dvb/ttpci/Kconfig
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -104,6 +104,8 @@ config DVB_BUDGET_CI
select DVB_STV0297 if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
+ select DVB_STB0899 if !DVB_FE_CUSTOMISE
+ select DVB_STB6100 if !DVB_FE_CUSTOMISE
select DVB_LNBP21 if !DVB_FE_CUSTOMISE
select DVB_TDA10023 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE
@@ -131,6 +133,8 @@ config DVB_BUDGET_AV
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
select DVB_TDA10021 if !DVB_FE_CUSTOMISE
select DVB_TDA10023 if !DVB_FE_CUSTOMISE
+ select DVB_STB0899 if !DVB_FE_CUSTOMISE
+ select DVB_TDA8261 if !DVB_FE_CUSTOMISE
select DVB_TUA6100 if !DVB_FE_CUSTOMISE
help
Support for simple SAA7146 based DVB cards
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index 1032ea77837e..f996cef79ec1 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -35,6 +35,11 @@
#include "budget.h"
#include "stv0299.h"
+#include "stb0899_drv.h"
+#include "stb0899_reg.h"
+#include "stb0899_cfg.h"
+#include "tda8261.h"
+#include "tda8261_cfg.h"
#include "tda1002x.h"
#include "tda1004x.h"
#include "tua6100.h"
@@ -882,6 +887,281 @@ static struct stv0299_config philips_sd1878_config = {
.set_symbol_rate = philips_sd1878_ci_set_symbol_rate,
};
+/* KNC1 DVB-S (STB0899) Inittab */
+static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = {
+
+ { STB0899_DEV_ID , 0x81 },
+ { STB0899_DISCNTRL1 , 0x32 },
+ { STB0899_DISCNTRL2 , 0x80 },
+ { STB0899_DISRX_ST0 , 0x04 },
+ { STB0899_DISRX_ST1 , 0x00 },
+ { STB0899_DISPARITY , 0x00 },
+ { STB0899_DISFIFO , 0x00 },
+ { STB0899_DISSTATUS , 0x20 },
+ { STB0899_DISF22 , 0x8c },
+ { STB0899_DISF22RX , 0x9a },
+ { STB0899_SYSREG , 0x0b },
+ { STB0899_ACRPRESC , 0x11 },
+ { STB0899_ACRDIV1 , 0x0a },
+ { STB0899_ACRDIV2 , 0x05 },
+ { STB0899_DACR1 , 0x00 },
+ { STB0899_DACR2 , 0x00 },
+ { STB0899_OUTCFG , 0x00 },
+ { STB0899_MODECFG , 0x00 },
+ { STB0899_IRQSTATUS_3 , 0x30 },
+ { STB0899_IRQSTATUS_2 , 0x00 },
+ { STB0899_IRQSTATUS_1 , 0x00 },
+ { STB0899_IRQSTATUS_0 , 0x00 },
+ { STB0899_IRQMSK_3 , 0xf3 },
+ { STB0899_IRQMSK_2 , 0xfc },
+ { STB0899_IRQMSK_1 , 0xff },
+ { STB0899_IRQMSK_0 , 0xff },
+ { STB0899_IRQCFG , 0x00 },
+ { STB0899_I2CCFG , 0x88 },
+ { STB0899_I2CRPT , 0x58 }, /* Repeater=8, Stop=disabled */
+ { STB0899_IOPVALUE5 , 0x00 },
+ { STB0899_IOPVALUE4 , 0x20 },
+ { STB0899_IOPVALUE3 , 0xc9 },
+ { STB0899_IOPVALUE2 , 0x90 },
+ { STB0899_IOPVALUE1 , 0x40 },
+ { STB0899_IOPVALUE0 , 0x00 },
+ { STB0899_GPIO00CFG , 0x82 },
+ { STB0899_GPIO01CFG , 0x82 },
+ { STB0899_GPIO02CFG , 0x82 },
+ { STB0899_GPIO03CFG , 0x82 },
+ { STB0899_GPIO04CFG , 0x82 },
+ { STB0899_GPIO05CFG , 0x82 },
+ { STB0899_GPIO06CFG , 0x82 },
+ { STB0899_GPIO07CFG , 0x82 },
+ { STB0899_GPIO08CFG , 0x82 },
+ { STB0899_GPIO09CFG , 0x82 },
+ { STB0899_GPIO10CFG , 0x82 },
+ { STB0899_GPIO11CFG , 0x82 },
+ { STB0899_GPIO12CFG , 0x82 },
+ { STB0899_GPIO13CFG , 0x82 },
+ { STB0899_GPIO14CFG , 0x82 },
+ { STB0899_GPIO15CFG , 0x82 },
+ { STB0899_GPIO16CFG , 0x82 },
+ { STB0899_GPIO17CFG , 0x82 },
+ { STB0899_GPIO18CFG , 0x82 },
+ { STB0899_GPIO19CFG , 0x82 },
+ { STB0899_GPIO20CFG , 0x82 },
+ { STB0899_SDATCFG , 0xb8 },
+ { STB0899_SCLTCFG , 0xba },
+ { STB0899_AGCRFCFG , 0x08 }, /* 0x1c */
+ { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */
+ { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */
+ { STB0899_DIRCLKCFG , 0x82 },
+ { STB0899_CLKOUT27CFG , 0x7e },
+ { STB0899_STDBYCFG , 0x82 },
+ { STB0899_CS0CFG , 0x82 },
+ { STB0899_CS1CFG , 0x82 },
+ { STB0899_DISEQCOCFG , 0x20 },
+ { STB0899_GPIO32CFG , 0x82 },
+ { STB0899_GPIO33CFG , 0x82 },
+ { STB0899_GPIO34CFG , 0x82 },
+ { STB0899_GPIO35CFG , 0x82 },
+ { STB0899_GPIO36CFG , 0x82 },
+ { STB0899_GPIO37CFG , 0x82 },
+ { STB0899_GPIO38CFG , 0x82 },
+ { STB0899_GPIO39CFG , 0x82 },
+ { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
+ { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
+ { STB0899_FILTCTRL , 0x00 },
+ { STB0899_SYSCTRL , 0x00 },
+ { STB0899_STOPCLK1 , 0x20 },
+ { STB0899_STOPCLK2 , 0x00 },
+ { STB0899_INTBUFSTATUS , 0x00 },
+ { STB0899_INTBUFCTRL , 0x0a },
+ { 0xffff , 0xff },
+};
+
+static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = {
+ { STB0899_DEMOD , 0x00 },
+ { STB0899_RCOMPC , 0xc9 },
+ { STB0899_AGC1CN , 0x41 },
+ { STB0899_AGC1REF , 0x08 },
+ { STB0899_RTC , 0x7a },
+ { STB0899_TMGCFG , 0x4e },
+ { STB0899_AGC2REF , 0x33 },
+ { STB0899_TLSR , 0x84 },
+ { STB0899_CFD , 0xee },
+ { STB0899_ACLC , 0x87 },
+ { STB0899_BCLC , 0x94 },
+ { STB0899_EQON , 0x41 },
+ { STB0899_LDT , 0xdd },
+ { STB0899_LDT2 , 0xc9 },
+ { STB0899_EQUALREF , 0xb4 },
+ { STB0899_TMGRAMP , 0x10 },
+ { STB0899_TMGTHD , 0x30 },
+ { STB0899_IDCCOMP , 0xfb },
+ { STB0899_QDCCOMP , 0x03 },
+ { STB0899_POWERI , 0x3b },
+ { STB0899_POWERQ , 0x3d },
+ { STB0899_RCOMP , 0x81 },
+ { STB0899_AGCIQIN , 0x80 },
+ { STB0899_AGC2I1 , 0x04 },
+ { STB0899_AGC2I2 , 0xf5 },
+ { STB0899_TLIR , 0x25 },
+ { STB0899_RTF , 0x80 },
+ { STB0899_DSTATUS , 0x00 },
+ { STB0899_LDI , 0xca },
+ { STB0899_CFRM , 0xf1 },
+ { STB0899_CFRL , 0xf3 },
+ { STB0899_NIRM , 0x2a },
+ { STB0899_NIRL , 0x05 },
+ { STB0899_ISYMB , 0x17 },
+ { STB0899_QSYMB , 0xfa },
+ { STB0899_SFRH , 0x2f },
+ { STB0899_SFRM , 0x68 },
+ { STB0899_SFRL , 0x40 },
+ { STB0899_SFRUPH , 0x2f },
+ { STB0899_SFRUPM , 0x68 },
+ { STB0899_SFRUPL , 0x40 },
+ { STB0899_EQUAI1 , 0xfd },
+ { STB0899_EQUAQ1 , 0x04 },
+ { STB0899_EQUAI2 , 0x0f },
+ { STB0899_EQUAQ2 , 0xff },
+ { STB0899_EQUAI3 , 0xdf },
+ { STB0899_EQUAQ3 , 0xfa },
+ { STB0899_EQUAI4 , 0x37 },
+ { STB0899_EQUAQ4 , 0x0d },
+ { STB0899_EQUAI5 , 0xbd },
+ { STB0899_EQUAQ5 , 0xf7 },
+ { STB0899_DSTATUS2 , 0x00 },
+ { STB0899_VSTATUS , 0x00 },
+ { STB0899_VERROR , 0xff },
+ { STB0899_IQSWAP , 0x2a },
+ { STB0899_ECNT1M , 0x00 },
+ { STB0899_ECNT1L , 0x00 },
+ { STB0899_ECNT2M , 0x00 },
+ { STB0899_ECNT2L , 0x00 },
+ { STB0899_ECNT3M , 0x00 },
+ { STB0899_ECNT3L , 0x00 },
+ { STB0899_FECAUTO1 , 0x06 },
+ { STB0899_FECM , 0x01 },
+ { STB0899_VTH12 , 0xf0 },
+ { STB0899_VTH23 , 0xa0 },
+ { STB0899_VTH34 , 0x78 },
+ { STB0899_VTH56 , 0x4e },
+ { STB0899_VTH67 , 0x48 },
+ { STB0899_VTH78 , 0x38 },
+ { STB0899_PRVIT , 0xff },
+ { STB0899_VITSYNC , 0x19 },
+ { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+ { STB0899_TSULC , 0x42 },
+ { STB0899_RSLLC , 0x40 },
+ { STB0899_TSLPL , 0x12 },
+ { STB0899_TSCFGH , 0x0c },
+ { STB0899_TSCFGM , 0x00 },
+ { STB0899_TSCFGL , 0x0c },
+ { STB0899_TSOUT , 0x0d }, /* 0x0d for CAM */
+ { STB0899_RSSYNCDEL , 0x00 },
+ { STB0899_TSINHDELH , 0x02 },
+ { STB0899_TSINHDELM , 0x00 },
+ { STB0899_TSINHDELL , 0x00 },
+ { STB0899_TSLLSTKM , 0x00 },
+ { STB0899_TSLLSTKL , 0x00 },
+ { STB0899_TSULSTKM , 0x00 },
+ { STB0899_TSULSTKL , 0xab },
+ { STB0899_PCKLENUL , 0x00 },
+ { STB0899_PCKLENLL , 0xcc },
+ { STB0899_RSPCKLEN , 0xcc },
+ { STB0899_TSSTATUS , 0x80 },
+ { STB0899_ERRCTRL1 , 0xb6 },
+ { STB0899_ERRCTRL2 , 0x96 },
+ { STB0899_ERRCTRL3 , 0x89 },
+ { STB0899_DMONMSK1 , 0x27 },
+ { STB0899_DMONMSK0 , 0x03 },
+ { STB0899_DEMAPVIT , 0x5c },
+ { STB0899_PLPARM , 0x1f },
+ { STB0899_PDELCTRL , 0x48 },
+ { STB0899_PDELCTRL2 , 0x00 },
+ { STB0899_BBHCTRL1 , 0x00 },
+ { STB0899_BBHCTRL2 , 0x00 },
+ { STB0899_HYSTTHRESH , 0x77 },
+ { STB0899_MATCSTM , 0x00 },
+ { STB0899_MATCSTL , 0x00 },
+ { STB0899_UPLCSTM , 0x00 },
+ { STB0899_UPLCSTL , 0x00 },
+ { STB0899_DFLCSTM , 0x00 },
+ { STB0899_DFLCSTL , 0x00 },
+ { STB0899_SYNCCST , 0x00 },
+ { STB0899_SYNCDCSTM , 0x00 },
+ { STB0899_SYNCDCSTL , 0x00 },
+ { STB0899_ISI_ENTRY , 0x00 },
+ { STB0899_ISI_BIT_EN , 0x00 },
+ { STB0899_MATSTRM , 0x00 },
+ { STB0899_MATSTRL , 0x00 },
+ { STB0899_UPLSTRM , 0x00 },
+ { STB0899_UPLSTRL , 0x00 },
+ { STB0899_DFLSTRM , 0x00 },
+ { STB0899_DFLSTRL , 0x00 },
+ { STB0899_SYNCSTR , 0x00 },
+ { STB0899_SYNCDSTRM , 0x00 },
+ { STB0899_SYNCDSTRL , 0x00 },
+ { STB0899_CFGPDELSTATUS1 , 0x10 },
+ { STB0899_CFGPDELSTATUS2 , 0x00 },
+ { STB0899_BBFERRORM , 0x00 },
+ { STB0899_BBFERRORL , 0x00 },
+ { STB0899_UPKTERRORM , 0x00 },
+ { STB0899_UPKTERRORL , 0x00 },
+ { 0xffff , 0xff },
+};
+
+/* STB0899 demodulator config for the KNC1 and clones */
+static struct stb0899_config knc1_dvbs2_config = {
+ .init_dev = knc1_stb0899_s1_init_1,
+ .init_s2_demod = stb0899_s2_init_2,
+ .init_s1_demod = knc1_stb0899_s1_init_3,
+ .init_s2_fec = stb0899_s2_init_4,
+ .init_tst = stb0899_s1_init_5,
+
+ .postproc = NULL,
+
+ .demod_address = 0x68,
+// .ts_output_mode = STB0899_OUT_PARALLEL, /* types = SERIAL/PARALLEL */
+ .block_sync_mode = STB0899_SYNC_FORCED, /* DSS, SYNC_FORCED/UNSYNCED */
+// .ts_pfbit_toggle = STB0899_MPEG_NORMAL, /* DirecTV, MPEG toggling seq */
+
+ .xtal_freq = 27000000,
+ .inversion = IQ_SWAP_OFF, /* 1 */
+
+ .lo_clk = 76500000,
+ .hi_clk = 90000000,
+
+ .esno_ave = STB0899_DVBS2_ESNO_AVE,
+ .esno_quant = STB0899_DVBS2_ESNO_QUANT,
+ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
+ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
+ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
+ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
+ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
+ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
+ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
+
+ .tuner_get_frequency = tda8261_get_frequency,
+ .tuner_set_frequency = tda8261_set_frequency,
+ .tuner_set_bandwidth = NULL,
+ .tuner_get_bandwidth = tda8261_get_bandwidth,
+ .tuner_set_rfsiggain = NULL
+};
+
+/*
+ * SD1878/SHA tuner config
+ * 1F, Single I/P, Horizontal mount, High Sensitivity
+ */
+static const struct tda8261_config sd1878c_config = {
+// .name = "SD1878/SHA",
+ .addr = 0x60,
+ .step_size = TDA8261_STEP_1000 /* kHz */
+};
+
static u8 read_pwm(struct budget_av *budget_av)
{
u8 b = 0xff;
@@ -905,8 +1185,11 @@ static u8 read_pwm(struct budget_av *budget_av)
#define SUBID_DVBS_TV_STAR 0x0014
#define SUBID_DVBS_TV_STAR_PLUS_X4 0x0015
#define SUBID_DVBS_TV_STAR_CI 0x0016
+#define SUBID_DVBS2_KNC1 0x0018
+#define SUBID_DVBS2_KNC1_OEM 0x0019
#define SUBID_DVBS_EASYWATCH_1 0x001a
#define SUBID_DVBS_EASYWATCH_2 0x001b
+#define SUBID_DVBS2_EASYWATCH 0x001d
#define SUBID_DVBS_EASYWATCH 0x001e
#define SUBID_DVBC_EASYWATCH 0x002a
@@ -941,6 +1224,9 @@ static void frontend_init(struct budget_av *budget_av)
case SUBID_DVBT_KNC1_PLUS:
case SUBID_DVBC_EASYWATCH:
case SUBID_DVBC_KNC1_PLUS_MK3:
+ case SUBID_DVBS2_KNC1:
+ case SUBID_DVBS2_KNC1_OEM:
+ case SUBID_DVBS2_EASYWATCH:
saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
break;
}
@@ -993,7 +1279,14 @@ static void frontend_init(struct budget_av *budget_av)
fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
}
break;
+ case SUBID_DVBS2_KNC1:
+ case SUBID_DVBS2_KNC1_OEM:
+ case SUBID_DVBS2_EASYWATCH:
+ budget_av->reinitialise_demod = 1;
+ if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap)))
+ dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap);
+ break;
case SUBID_DVBS_CINERGY1200:
fe = dvb_attach(stv0299_attach, &cinergy_1200s_config,
&budget_av->budget.i2c_adap);
@@ -1260,6 +1553,8 @@ static struct saa7146_ext_vv vv_data = {
static struct saa7146_extension budget_extension;
MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S);
+MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2);
+MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2);
MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C);
MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T);
MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR);
@@ -1290,6 +1585,9 @@ static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014),
MAKE_EXTENSION_PCI(knc1spx4, 0x1894, 0x0015),
MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016),
+ MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0018),
+ MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0019),
+ MAKE_EXTENSION_PCI(sates2, 0x1894, 0x001d),
MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e),
MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a),
MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b),
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index 0a5aad45435d..3507463fdac9 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -43,6 +43,11 @@
#include "stv0299.h"
#include "stv0297.h"
#include "tda1004x.h"
+#include "stb0899_drv.h"
+#include "stb0899_reg.h"
+#include "stb0899_cfg.h"
+#include "stb6100.h"
+#include "stb6100_cfg.h"
#include "lnbp21.h"
#include "bsbe1.h"
#include "bsru6.h"
@@ -1071,7 +1076,271 @@ static struct tda10023_config tda10023_config = {
.deltaf = 0xa511,
};
+/* TT S2-3200 DVB-S (STB0899) Inittab */
+static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = {
+
+ { STB0899_DEV_ID , 0x81 },
+ { STB0899_DISCNTRL1 , 0x32 },
+ { STB0899_DISCNTRL2 , 0x80 },
+ { STB0899_DISRX_ST0 , 0x04 },
+ { STB0899_DISRX_ST1 , 0x00 },
+ { STB0899_DISPARITY , 0x00 },
+ { STB0899_DISFIFO , 0x00 },
+ { STB0899_DISSTATUS , 0x20 },
+ { STB0899_DISF22 , 0x8c },
+ { STB0899_DISF22RX , 0x9a },
+ { STB0899_SYSREG , 0x0b },
+ { STB0899_ACRPRESC , 0x11 },
+ { STB0899_ACRDIV1 , 0x0a },
+ { STB0899_ACRDIV2 , 0x05 },
+ { STB0899_DACR1 , 0x00 },
+ { STB0899_DACR2 , 0x00 },
+ { STB0899_OUTCFG , 0x00 },
+ { STB0899_MODECFG , 0x00 },
+ { STB0899_IRQSTATUS_3 , 0x30 },
+ { STB0899_IRQSTATUS_2 , 0x00 },
+ { STB0899_IRQSTATUS_1 , 0x00 },
+ { STB0899_IRQSTATUS_0 , 0x00 },
+ { STB0899_IRQMSK_3 , 0xf3 },
+ { STB0899_IRQMSK_2 , 0xfc },
+ { STB0899_IRQMSK_1 , 0xff },
+ { STB0899_IRQMSK_0 , 0xff },
+ { STB0899_IRQCFG , 0x00 },
+ { STB0899_I2CCFG , 0x88 },
+ { STB0899_I2CRPT , 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */
+ { STB0899_IOPVALUE5 , 0x00 },
+ { STB0899_IOPVALUE4 , 0x20 },
+ { STB0899_IOPVALUE3 , 0xc9 },
+ { STB0899_IOPVALUE2 , 0x90 },
+ { STB0899_IOPVALUE1 , 0x40 },
+ { STB0899_IOPVALUE0 , 0x00 },
+ { STB0899_GPIO00CFG , 0x82 },
+ { STB0899_GPIO01CFG , 0x82 },
+ { STB0899_GPIO02CFG , 0x82 },
+ { STB0899_GPIO03CFG , 0x82 },
+ { STB0899_GPIO04CFG , 0x82 },
+ { STB0899_GPIO05CFG , 0x82 },
+ { STB0899_GPIO06CFG , 0x82 },
+ { STB0899_GPIO07CFG , 0x82 },
+ { STB0899_GPIO08CFG , 0x82 },
+ { STB0899_GPIO09CFG , 0x82 },
+ { STB0899_GPIO10CFG , 0x82 },
+ { STB0899_GPIO11CFG , 0x82 },
+ { STB0899_GPIO12CFG , 0x82 },
+ { STB0899_GPIO13CFG , 0x82 },
+ { STB0899_GPIO14CFG , 0x82 },
+ { STB0899_GPIO15CFG , 0x82 },
+ { STB0899_GPIO16CFG , 0x82 },
+ { STB0899_GPIO17CFG , 0x82 },
+ { STB0899_GPIO18CFG , 0x82 },
+ { STB0899_GPIO19CFG , 0x82 },
+ { STB0899_GPIO20CFG , 0x82 },
+ { STB0899_SDATCFG , 0xb8 },
+ { STB0899_SCLTCFG , 0xba },
+ { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */
+ { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */
+ { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */
+ { STB0899_DIRCLKCFG , 0x82 },
+ { STB0899_CLKOUT27CFG , 0x7e },
+ { STB0899_STDBYCFG , 0x82 },
+ { STB0899_CS0CFG , 0x82 },
+ { STB0899_CS1CFG , 0x82 },
+ { STB0899_DISEQCOCFG , 0x20 },
+ { STB0899_GPIO32CFG , 0x82 },
+ { STB0899_GPIO33CFG , 0x82 },
+ { STB0899_GPIO34CFG , 0x82 },
+ { STB0899_GPIO35CFG , 0x82 },
+ { STB0899_GPIO36CFG , 0x82 },
+ { STB0899_GPIO37CFG , 0x82 },
+ { STB0899_GPIO38CFG , 0x82 },
+ { STB0899_GPIO39CFG , 0x82 },
+ { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
+ { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
+ { STB0899_FILTCTRL , 0x00 },
+ { STB0899_SYSCTRL , 0x00 },
+ { STB0899_STOPCLK1 , 0x20 },
+ { STB0899_STOPCLK2 , 0x00 },
+ { STB0899_INTBUFSTATUS , 0x00 },
+ { STB0899_INTBUFCTRL , 0x0a },
+ { 0xffff , 0xff },
+};
+
+static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = {
+ { STB0899_DEMOD , 0x00 },
+ { STB0899_RCOMPC , 0xc9 },
+ { STB0899_AGC1CN , 0x41 },
+ { STB0899_AGC1REF , 0x10 },
+ { STB0899_RTC , 0x7a },
+ { STB0899_TMGCFG , 0x4e },
+ { STB0899_AGC2REF , 0x34 },
+ { STB0899_TLSR , 0x84 },
+ { STB0899_CFD , 0xc7 },
+ { STB0899_ACLC , 0x87 },
+ { STB0899_BCLC , 0x94 },
+ { STB0899_EQON , 0x41 },
+ { STB0899_LDT , 0xdd },
+ { STB0899_LDT2 , 0xc9 },
+ { STB0899_EQUALREF , 0xb4 },
+ { STB0899_TMGRAMP , 0x10 },
+ { STB0899_TMGTHD , 0x30 },
+ { STB0899_IDCCOMP , 0xfb },
+ { STB0899_QDCCOMP , 0x03 },
+ { STB0899_POWERI , 0x3b },
+ { STB0899_POWERQ , 0x3d },
+ { STB0899_RCOMP , 0x81 },
+ { STB0899_AGCIQIN , 0x80 },
+ { STB0899_AGC2I1 , 0x04 },
+ { STB0899_AGC2I2 , 0xf5 },
+ { STB0899_TLIR , 0x25 },
+ { STB0899_RTF , 0x80 },
+ { STB0899_DSTATUS , 0x00 },
+ { STB0899_LDI , 0xca },
+ { STB0899_CFRM , 0xf1 },
+ { STB0899_CFRL , 0xf3 },
+ { STB0899_NIRM , 0x2a },
+ { STB0899_NIRL , 0x05 },
+ { STB0899_ISYMB , 0x17 },
+ { STB0899_QSYMB , 0xfa },
+ { STB0899_SFRH , 0x2f },
+ { STB0899_SFRM , 0x68 },
+ { STB0899_SFRL , 0x40 },
+ { STB0899_SFRUPH , 0x2f },
+ { STB0899_SFRUPM , 0x68 },
+ { STB0899_SFRUPL , 0x40 },
+ { STB0899_EQUAI1 , 0xfd },
+ { STB0899_EQUAQ1 , 0x04 },
+ { STB0899_EQUAI2 , 0x0f },
+ { STB0899_EQUAQ2 , 0xff },
+ { STB0899_EQUAI3 , 0xdf },
+ { STB0899_EQUAQ3 , 0xfa },
+ { STB0899_EQUAI4 , 0x37 },
+ { STB0899_EQUAQ4 , 0x0d },
+ { STB0899_EQUAI5 , 0xbd },
+ { STB0899_EQUAQ5 , 0xf7 },
+ { STB0899_DSTATUS2 , 0x00 },
+ { STB0899_VSTATUS , 0x00 },
+ { STB0899_VERROR , 0xff },
+ { STB0899_IQSWAP , 0x2a },
+ { STB0899_ECNT1M , 0x00 },
+ { STB0899_ECNT1L , 0x00 },
+ { STB0899_ECNT2M , 0x00 },
+ { STB0899_ECNT2L , 0x00 },
+ { STB0899_ECNT3M , 0x00 },
+ { STB0899_ECNT3L , 0x00 },
+ { STB0899_FECAUTO1 , 0x06 },
+ { STB0899_FECM , 0x01 },
+ { STB0899_VTH12 , 0xf0 },
+ { STB0899_VTH23 , 0xa0 },
+ { STB0899_VTH34 , 0x78 },
+ { STB0899_VTH56 , 0x4e },
+ { STB0899_VTH67 , 0x48 },
+ { STB0899_VTH78 , 0x38 },
+ { STB0899_PRVIT , 0xff },
+ { STB0899_VITSYNC , 0x19 },
+ { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+ { STB0899_TSULC , 0x42 },
+ { STB0899_RSLLC , 0x40 },
+ { STB0899_TSLPL , 0x12 },
+ { STB0899_TSCFGH , 0x0c },
+ { STB0899_TSCFGM , 0x00 },
+ { STB0899_TSCFGL , 0x0c },
+ { STB0899_TSOUT , 0x0d }, /* 0x0d for CAM */
+ { STB0899_RSSYNCDEL , 0x00 },
+ { STB0899_TSINHDELH , 0x02 },
+ { STB0899_TSINHDELM , 0x00 },
+ { STB0899_TSINHDELL , 0x00 },
+ { STB0899_TSLLSTKM , 0x00 },
+ { STB0899_TSLLSTKL , 0x00 },
+ { STB0899_TSULSTKM , 0x00 },
+ { STB0899_TSULSTKL , 0xab },
+ { STB0899_PCKLENUL , 0x00 },
+ { STB0899_PCKLENLL , 0xcc },
+ { STB0899_RSPCKLEN , 0xcc },
+ { STB0899_TSSTATUS , 0x80 },
+ { STB0899_ERRCTRL1 , 0xb6 },
+ { STB0899_ERRCTRL2 , 0x96 },
+ { STB0899_ERRCTRL3 , 0x89 },
+ { STB0899_DMONMSK1 , 0x27 },
+ { STB0899_DMONMSK0 , 0x03 },
+ { STB0899_DEMAPVIT , 0x5c },
+ { STB0899_PLPARM , 0x1f },
+ { STB0899_PDELCTRL , 0x48 },
+ { STB0899_PDELCTRL2 , 0x00 },
+ { STB0899_BBHCTRL1 , 0x00 },
+ { STB0899_BBHCTRL2 , 0x00 },
+ { STB0899_HYSTTHRESH , 0x77 },
+ { STB0899_MATCSTM , 0x00 },
+ { STB0899_MATCSTL , 0x00 },
+ { STB0899_UPLCSTM , 0x00 },
+ { STB0899_UPLCSTL , 0x00 },
+ { STB0899_DFLCSTM , 0x00 },
+ { STB0899_DFLCSTL , 0x00 },
+ { STB0899_SYNCCST , 0x00 },
+ { STB0899_SYNCDCSTM , 0x00 },
+ { STB0899_SYNCDCSTL , 0x00 },
+ { STB0899_ISI_ENTRY , 0x00 },
+ { STB0899_ISI_BIT_EN , 0x00 },
+ { STB0899_MATSTRM , 0x00 },
+ { STB0899_MATSTRL , 0x00 },
+ { STB0899_UPLSTRM , 0x00 },
+ { STB0899_UPLSTRL , 0x00 },
+ { STB0899_DFLSTRM , 0x00 },
+ { STB0899_DFLSTRL , 0x00 },
+ { STB0899_SYNCSTR , 0x00 },
+ { STB0899_SYNCDSTRM , 0x00 },
+ { STB0899_SYNCDSTRL , 0x00 },
+ { STB0899_CFGPDELSTATUS1 , 0x10 },
+ { STB0899_CFGPDELSTATUS2 , 0x00 },
+ { STB0899_BBFERRORM , 0x00 },
+ { STB0899_BBFERRORL , 0x00 },
+ { STB0899_UPKTERRORM , 0x00 },
+ { STB0899_UPKTERRORL , 0x00 },
+ { 0xffff , 0xff },
+};
+static struct stb0899_config tt3200_config = {
+ .init_dev = tt3200_stb0899_s1_init_1,
+ .init_s2_demod = stb0899_s2_init_2,
+ .init_s1_demod = tt3200_stb0899_s1_init_3,
+ .init_s2_fec = stb0899_s2_init_4,
+ .init_tst = stb0899_s1_init_5,
+
+ .postproc = NULL,
+
+ .demod_address = 0x68,
+
+ .xtal_freq = 27000000,
+ .inversion = IQ_SWAP_ON, /* 1 */
+
+ .lo_clk = 76500000,
+ .hi_clk = 99000000,
+
+ .esno_ave = STB0899_DVBS2_ESNO_AVE,
+ .esno_quant = STB0899_DVBS2_ESNO_QUANT,
+ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
+ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
+ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
+ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
+ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
+ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
+ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
+
+ .tuner_get_frequency = stb6100_get_frequency,
+ .tuner_set_frequency = stb6100_set_frequency,
+ .tuner_set_bandwidth = stb6100_set_bandwidth,
+ .tuner_get_bandwidth = stb6100_get_bandwidth,
+ .tuner_set_rfsiggain = NULL
+};
+
+struct stb6100_config tt3200_stb6100_config = {
+ .tuner_address = 0x60,
+ .refclock = 27000000,
+};
static void frontend_init(struct budget_ci *budget_ci)
{
@@ -1152,6 +1421,46 @@ static void frontend_init(struct budget_ci *budget_ci)
}
}
break;
+
+ case 0x1019: // TT S2-3200 PCI
+ /*
+ * NOTE! on some STB0899 versions, the internal PLL takes a longer time
+ * to settle, aka LOCK. On the older revisions of the chip, we don't see
+ * this, as a result on the newer chips the entire clock tree, will not
+ * be stable after a freshly POWER 'ed up situation.
+ * In this case, we should RESET the STB0899 (Active LOW) and wait for
+ * PLL stabilization.
+ *
+ * On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is
+ * connected to the SAA7146 GPIO, GPIO2, Pin 142
+ */
+ /* Reset Demodulator */
+ saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTLO);
+ /* Wait for everything to die */
+ msleep(50);
+ /* Pull it up out of Reset state */
+ saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTHI);
+ /* Wait for PLL to stabilize */
+ msleep(250);
+ /*
+ * PLL state should be stable now. Ideally, we should check
+ * for PLL LOCK status. But well, never mind!
+ */
+ budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap);
+ if (budget_ci->budget.dvb_frontend) {
+ if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) {
+ if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) {
+ printk("%s: No LNBP21 found!\n", __FUNCTION__);
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
+ }
+ } else {
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
+ }
+ }
+ break;
+
}
if (budget_ci->budget.dvb_frontend == NULL) {
@@ -1242,6 +1551,7 @@ MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT);
MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT);
MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI", BUDGET_TT);
static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c),
@@ -1251,6 +1561,7 @@ static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012),
MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017),
MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a),
+ MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019),
{
.vendor = 0,
}
diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h
index 86435bf16260..3ad0c6789ba7 100644
--- a/drivers/media/dvb/ttpci/budget.h
+++ b/drivers/media/dvb/ttpci/budget.h
@@ -103,6 +103,7 @@ static struct saa7146_pci_extension_data x_var = { \
#define BUDGET_CIN1200C_MK3 15
#define BUDGET_KNC1C_MK3 16
#define BUDGET_KNC1CP_MK3 17
+#define BUDGET_KNC1S2 18
#define BUDGET_VIDEO_PORTA 0
#define BUDGET_VIDEO_PORTB 1
diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c
index a5ca176a7b08..5474a22c1b22 100644
--- a/drivers/media/radio/dsbr100.c
+++ b/drivers/media/radio/dsbr100.c
@@ -1,5 +1,5 @@
-/* A driver for the D-Link DSB-R100 USB radio. The R100 plugs
- into both the USB and an analog audio input, so this thing
+/* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21.
+ The device plugs into both the USB and an analog audio input, so this thing
only deals with initialisation and frequency setting, the
audio data has to be handled by a sound driver.
@@ -33,6 +33,10 @@
History:
+ Version 0.44:
+ Add suspend/resume functions, fix unplug of device,
+ a lot of cleanups and fixes by Alexey Klimov <klimov.linux@gmail.com>
+
Version 0.43:
Oliver Neukum: avoided DMA coherency issue
@@ -93,8 +97,8 @@
*/
#include <linux/version.h> /* for KERNEL_VERSION MACRO */
-#define DRIVER_VERSION "v0.41"
-#define RADIO_VERSION KERNEL_VERSION(0,4,1)
+#define DRIVER_VERSION "v0.44"
+#define RADIO_VERSION KERNEL_VERSION(0, 4, 4)
static struct v4l2_queryctrl radio_qctrl[] = {
{
@@ -104,7 +108,27 @@ static struct v4l2_queryctrl radio_qctrl[] = {
.maximum = 1,
.default_value = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
- }
+ },
+/* HINT: the disabled controls are only here to satify kradio and such apps */
+ { .id = V4L2_CID_AUDIO_VOLUME,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BASS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_LOUDNESS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
};
#define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
@@ -125,12 +149,16 @@ devices, that would be 76 and 91. */
#define FREQ_MAX 108.0
#define FREQ_MUL 16000
+#define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev)
static int usb_dsbr100_probe(struct usb_interface *intf,
const struct usb_device_id *id);
static void usb_dsbr100_disconnect(struct usb_interface *intf);
static int usb_dsbr100_open(struct inode *inode, struct file *file);
static int usb_dsbr100_close(struct inode *inode, struct file *file);
+static int usb_dsbr100_suspend(struct usb_interface *intf,
+ pm_message_t message);
+static int usb_dsbr100_resume(struct usb_interface *intf);
static int radio_nr = -1;
module_param(radio_nr, int, 0);
@@ -138,8 +166,9 @@ module_param(radio_nr, int, 0);
/* Data for one (physical) device */
struct dsbr100_device {
struct usb_device *usbdev;
- struct video_device *videodev;
+ struct video_device videodev;
u8 *transfer_buffer;
+ struct mutex lock; /* buffer locking */
int curfreq;
int stereo;
int users;
@@ -147,7 +176,6 @@ struct dsbr100_device {
int muted;
};
-
static struct usb_device_id usb_dsbr100_device_table [] = {
{ USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
{ } /* Terminating entry */
@@ -157,10 +185,14 @@ MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table);
/* USB subsystem interface */
static struct usb_driver usb_dsbr100_driver = {
- .name = "dsbr100",
- .probe = usb_dsbr100_probe,
- .disconnect = usb_dsbr100_disconnect,
- .id_table = usb_dsbr100_device_table,
+ .name = "dsbr100",
+ .probe = usb_dsbr100_probe,
+ .disconnect = usb_dsbr100_disconnect,
+ .id_table = usb_dsbr100_device_table,
+ .suspend = usb_dsbr100_suspend,
+ .resume = usb_dsbr100_resume,
+ .reset_resume = usb_dsbr100_resume,
+ .supports_autosuspend = 0,
};
/* Low-level device interface begins here */
@@ -168,95 +200,190 @@ static struct usb_driver usb_dsbr100_driver = {
/* switch on radio */
static int dsbr100_start(struct dsbr100_device *radio)
{
- if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x00, 0xC7, radio->transfer_buffer, 8, 300) < 0 ||
- usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- DSB100_ONOFF,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x01, 0x00, radio->transfer_buffer, 8, 300) < 0)
- return -1;
- radio->muted=0;
+ int retval;
+ int request;
+
+ mutex_lock(&radio->lock);
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, 0xC7, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = USB_REQ_GET_STATUS;
+ goto usb_control_msg_failed;
+ }
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_ONOFF,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x01, 0x00, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = DSB100_ONOFF;
+ goto usb_control_msg_failed;
+ }
+
+ radio->muted = 0;
+ mutex_unlock(&radio->lock);
return (radio->transfer_buffer)[0];
-}
+usb_control_msg_failed:
+ mutex_unlock(&radio->lock);
+ dev_err(&radio->usbdev->dev,
+ "%s - usb_control_msg returned %i, request %i\n",
+ __func__, retval, request);
+ return retval;
+
+}
/* switch off radio */
static int dsbr100_stop(struct dsbr100_device *radio)
{
- if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x16, 0x1C, radio->transfer_buffer, 8, 300) < 0 ||
- usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- DSB100_ONOFF,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x00, 0x00, radio->transfer_buffer, 8, 300) < 0)
- return -1;
- radio->muted=1;
+ int retval;
+ int request;
+
+ mutex_lock(&radio->lock);
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x16, 0x1C, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = USB_REQ_GET_STATUS;
+ goto usb_control_msg_failed;
+ }
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_ONOFF,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, 0x00, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = DSB100_ONOFF;
+ goto usb_control_msg_failed;
+ }
+
+ radio->muted = 1;
+ mutex_unlock(&radio->lock);
return (radio->transfer_buffer)[0];
+
+usb_control_msg_failed:
+ mutex_unlock(&radio->lock);
+ dev_err(&radio->usbdev->dev,
+ "%s - usb_control_msg returned %i, request %i\n",
+ __func__, retval, request);
+ return retval;
+
}
/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
static int dsbr100_setfreq(struct dsbr100_device *radio, int freq)
{
+ int retval;
+ int request;
+
freq = (freq / 16 * 80) / 1000 + 856;
- if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- DSB100_TUNE,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- (freq >> 8) & 0x00ff, freq & 0xff,
- radio->transfer_buffer, 8, 300) < 0 ||
- usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x96, 0xB7, radio->transfer_buffer, 8, 300) < 0 ||
- usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x00, 0x24, radio->transfer_buffer, 8, 300) < 0) {
- radio->stereo = -1;
- return -1;
+ mutex_lock(&radio->lock);
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_TUNE,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ (freq >> 8) & 0x00ff, freq & 0xff,
+ radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = DSB100_TUNE;
+ goto usb_control_msg_failed;
}
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x96, 0xB7, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = USB_REQ_GET_STATUS;
+ goto usb_control_msg_failed;
+ }
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, 0x24, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = USB_REQ_GET_STATUS;
+ goto usb_control_msg_failed;
+ }
+
radio->stereo = !((radio->transfer_buffer)[0] & 0x01);
+ mutex_unlock(&radio->lock);
return (radio->transfer_buffer)[0];
+
+usb_control_msg_failed:
+ radio->stereo = -1;
+ mutex_unlock(&radio->lock);
+ dev_err(&radio->usbdev->dev,
+ "%s - usb_control_msg returned %i, request %i\n",
+ __func__, retval, request);
+ return retval;
}
/* return the device status. This is, in effect, just whether it
sees a stereo signal or not. Pity. */
static void dsbr100_getstat(struct dsbr100_device *radio)
{
- if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ int retval;
+
+ mutex_lock(&radio->lock);
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
USB_REQ_GET_STATUS,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x00 , 0x24, radio->transfer_buffer, 8, 300) < 0)
+ 0x00 , 0x24, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
radio->stereo = -1;
- else
+ dev_err(&radio->usbdev->dev,
+ "%s - usb_control_msg returned %i, request %i\n",
+ __func__, retval, USB_REQ_GET_STATUS);
+ } else {
radio->stereo = !(radio->transfer_buffer[0] & 0x01);
-}
+ }
+ mutex_unlock(&radio->lock);
+}
/* USB subsystem interface begins here */
-/* handle unplugging of the device, release data structures
-if nothing keeps us from doing it. If something is still
-keeping us busy, the release callback of v4l will take care
-of releasing it. */
+/*
+ * Handle unplugging of the device.
+ * We call video_unregister_device in any case.
+ * The last function called in this procedure is
+ * usb_dsbr100_video_device_release
+ */
static void usb_dsbr100_disconnect(struct usb_interface *intf)
{
struct dsbr100_device *radio = usb_get_intfdata(intf);
usb_set_intfdata (intf, NULL);
- if (radio) {
- video_unregister_device(radio->videodev);
- radio->videodev = NULL;
- if (radio->users) {
- kfree(radio->transfer_buffer);
- kfree(radio);
- } else {
- radio->removed = 1;
- }
- }
+
+ mutex_lock(&radio->lock);
+ radio->removed = 1;
+ mutex_unlock(&radio->lock);
+
+ video_unregister_device(&radio->videodev);
}
@@ -276,6 +403,10 @@ static int vidioc_g_tuner(struct file *file, void *priv,
{
struct dsbr100_device *radio = video_drvdata(file);
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
if (v->index > 0)
return -EINVAL;
@@ -297,6 +428,12 @@ static int vidioc_g_tuner(struct file *file, void *priv,
static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
+ struct dsbr100_device *radio = video_drvdata(file);
+
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
if (v->index > 0)
return -EINVAL;
@@ -307,9 +444,15 @@ static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
struct dsbr100_device *radio = video_drvdata(file);
+ int retval;
+
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
radio->curfreq = f->frequency;
- if (dsbr100_setfreq(radio, radio->curfreq) == -1)
+ retval = dsbr100_setfreq(radio, radio->curfreq);
+ if (retval < 0)
dev_warn(&radio->usbdev->dev, "Set frequency failed\n");
return 0;
}
@@ -319,6 +462,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct dsbr100_device *radio = video_drvdata(file);
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
f->type = V4L2_TUNER_RADIO;
f->frequency = radio->curfreq;
return 0;
@@ -343,6 +490,10 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
{
struct dsbr100_device *radio = video_drvdata(file);
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
ctrl->value = radio->muted;
@@ -355,17 +506,24 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct dsbr100_device *radio = video_drvdata(file);
+ int retval;
+
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value) {
- if (dsbr100_stop(radio) == -1) {
+ retval = dsbr100_stop(radio);
+ if (retval < 0) {
dev_warn(&radio->usbdev->dev,
"Radio did not respond properly\n");
return -EBUSY;
}
} else {
- if (dsbr100_start(radio) == -1) {
+ retval = dsbr100_start(radio);
+ if (retval < 0) {
dev_warn(&radio->usbdev->dev,
"Radio did not respond properly\n");
return -EBUSY;
@@ -417,7 +575,8 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file)
radio->users = 1;
radio->muted = 1;
- if (dsbr100_start(radio) < 0) {
+ retval = dsbr100_start(radio);
+ if (retval < 0) {
dev_warn(&radio->usbdev->dev,
"Radio did not start up properly\n");
radio->users = 0;
@@ -426,9 +585,9 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file)
}
retval = dsbr100_setfreq(radio, radio->curfreq);
-
- if (retval == -1)
- printk(KERN_WARNING KBUILD_MODNAME ": Set frequency failed\n");
+ if (retval < 0)
+ dev_warn(&radio->usbdev->dev,
+ "set frequency failed\n");
unlock_kernel();
return 0;
@@ -437,17 +596,62 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file)
static int usb_dsbr100_close(struct inode *inode, struct file *file)
{
struct dsbr100_device *radio = video_drvdata(file);
+ int retval;
if (!radio)
return -ENODEV;
+
radio->users = 0;
- if (radio->removed) {
- kfree(radio->transfer_buffer);
- kfree(radio);
+ if (!radio->removed) {
+ retval = dsbr100_stop(radio);
+ if (retval < 0) {
+ dev_warn(&radio->usbdev->dev,
+ "dsbr100_stop failed\n");
+ }
+
}
return 0;
}
+/* Suspend device - stop device. */
+static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct dsbr100_device *radio = usb_get_intfdata(intf);
+ int retval;
+
+ retval = dsbr100_stop(radio);
+ if (retval < 0)
+ dev_warn(&intf->dev, "dsbr100_stop failed\n");
+
+ dev_info(&intf->dev, "going into suspend..\n");
+
+ return 0;
+}
+
+/* Resume device - start device. */
+static int usb_dsbr100_resume(struct usb_interface *intf)
+{
+ struct dsbr100_device *radio = usb_get_intfdata(intf);
+ int retval;
+
+ retval = dsbr100_start(radio);
+ if (retval < 0)
+ dev_warn(&intf->dev, "dsbr100_start failed\n");
+
+ dev_info(&intf->dev, "coming out of suspend..\n");
+
+ return 0;
+}
+
+/* free data structures */
+static void usb_dsbr100_video_device_release(struct video_device *videodev)
+{
+ struct dsbr100_device *radio = videodev_to_radio(videodev);
+
+ kfree(radio->transfer_buffer);
+ kfree(radio);
+}
+
/* File system interface */
static const struct file_operations usb_dsbr100_fops = {
.owner = THIS_MODULE,
@@ -476,19 +680,19 @@ static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
};
/* V4L2 interface */
-static struct video_device dsbr100_videodev_template = {
+static struct video_device dsbr100_videodev_data = {
.name = "D-Link DSB-R 100",
.fops = &usb_dsbr100_fops,
.ioctl_ops = &usb_dsbr100_ioctl_ops,
- .release = video_device_release,
+ .release = usb_dsbr100_video_device_release,
};
-/* check if the device is present and register with v4l and
-usb if it is */
+/* check if the device is present and register with v4l and usb if it is */
static int usb_dsbr100_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct dsbr100_device *radio;
+ int retval;
radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL);
@@ -501,23 +705,18 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
kfree(radio);
return -ENOMEM;
}
- radio->videodev = video_device_alloc();
- if (!(radio->videodev)) {
- kfree(radio->transfer_buffer);
- kfree(radio);
- return -ENOMEM;
- }
- memcpy(radio->videodev, &dsbr100_videodev_template,
- sizeof(dsbr100_videodev_template));
+ mutex_init(&radio->lock);
+ radio->videodev = dsbr100_videodev_data;
+
radio->removed = 0;
radio->users = 0;
radio->usbdev = interface_to_usbdev(intf);
radio->curfreq = FREQ_MIN * FREQ_MUL;
- video_set_drvdata(radio->videodev, radio);
- if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr) < 0) {
- dev_warn(&intf->dev, "Could not register video device\n");
- video_device_release(radio->videodev);
+ video_set_drvdata(&radio->videodev, radio);
+ retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr);
+ if (retval < 0) {
+ dev_err(&intf->dev, "couldn't register video device\n");
kfree(radio->transfer_buffer);
kfree(radio);
return -EIO;
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index 9305e958fc66..dd6d3dfcd7d2 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -1,7 +1,7 @@
/* radiotrack (radioreveal) driver for Linux radio support
* (c) 1997 M. Kirkwood
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
- * Converted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
*
* History:
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
index 0490a1fa999d..bfd37f38b9ab 100644
--- a/drivers/media/radio/radio-cadet.c
+++ b/drivers/media/radio/radio-cadet.c
@@ -23,7 +23,7 @@
* 2002-01-17 Adam Belay <ambx1@neo.rr.com>
* Updated to latest pnp code
*
- * 2003-01-31 Alan Cox <alan@redhat.com>
+ * 2003-01-31 Alan Cox <alan@lxorguk.ukuu.org.uk>
* Cleaned up locking, delay code, general odds and ends
*
* 2006-07-30 Hans J. Koch <koch@hjk-az.de>
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index d131a5d38128..e13118da307b 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -8,7 +8,7 @@
* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
*
* Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
- * Converted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
*
* TODO: Allow for more than one of these foolish entities :-)
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
index 256cbeffdcb6..e730eddb2bb5 100644
--- a/drivers/media/radio/radio-mr800.c
+++ b/drivers/media/radio/radio-mr800.c
@@ -72,6 +72,11 @@ MODULE_LICENSE("GPL");
#define USB_AMRADIO_VENDOR 0x07ca
#define USB_AMRADIO_PRODUCT 0xb800
+/* dev_warn macro with driver name */
+#define MR800_DRIVER_NAME "radio-mr800"
+#define amradio_dev_warn(dev, fmt, arg...) \
+ dev_warn(dev, MR800_DRIVER_NAME " - " fmt, ##arg)
+
/* Probably USB_TIMEOUT should be modified in module parameter */
#define BUFFER_LENGTH 8
#define USB_TIMEOUT 500
@@ -154,14 +159,14 @@ MODULE_DEVICE_TABLE(usb, usb_amradio_device_table);
/* USB subsystem interface */
static struct usb_driver usb_amradio_driver = {
- .name = "radio-mr800",
+ .name = MR800_DRIVER_NAME,
.probe = usb_amradio_probe,
.disconnect = usb_amradio_disconnect,
.suspend = usb_amradio_suspend,
.resume = usb_amradio_resume,
.reset_resume = usb_amradio_resume,
.id_table = usb_amradio_device_table,
- .supports_autosuspend = 1,
+ .supports_autosuspend = 0,
};
/* switch on radio. Send 8 bytes to device. */
@@ -202,6 +207,10 @@ static int amradio_stop(struct amradio_device *radio)
int retval;
int size;
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
mutex_lock(&radio->lock);
radio->buffer[0] = 0x00;
@@ -235,6 +244,10 @@ static int amradio_setfreq(struct amradio_device *radio, int freq)
int size;
unsigned short freq_send = 0x13 + (freq >> 3) / 25;
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
mutex_lock(&radio->lock);
radio->buffer[0] = 0x00;
@@ -288,18 +301,12 @@ static void usb_amradio_disconnect(struct usb_interface *intf)
{
struct amradio_device *radio = usb_get_intfdata(intf);
- usb_set_intfdata(intf, NULL);
+ mutex_lock(&radio->lock);
+ radio->removed = 1;
+ mutex_unlock(&radio->lock);
- if (radio) {
- video_unregister_device(radio->videodev);
- radio->videodev = NULL;
- if (radio->users) {
- kfree(radio->buffer);
- kfree(radio);
- } else {
- radio->removed = 1;
- }
- }
+ usb_set_intfdata(intf, NULL);
+ video_unregister_device(radio->videodev);
}
/* vidioc_querycap - query device capabilities */
@@ -320,6 +327,10 @@ static int vidioc_g_tuner(struct file *file, void *priv,
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
if (v->index > 0)
return -EINVAL;
@@ -346,6 +357,12 @@ static int vidioc_g_tuner(struct file *file, void *priv,
static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
if (v->index > 0)
return -EINVAL;
return 0;
@@ -357,9 +374,14 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
radio->curfreq = f->frequency;
if (amradio_setfreq(radio, radio->curfreq) < 0)
- warn("Set frequency failed");
+ amradio_dev_warn(&radio->videodev->dev,
+ "set frequency failed\n");
return 0;
}
@@ -369,6 +391,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
f->type = V4L2_TUNER_RADIO;
f->frequency = radio->curfreq;
return 0;
@@ -382,8 +408,7 @@ static int vidioc_queryctrl(struct file *file, void *priv,
for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
if (qc->id && qc->id == radio_qctrl[i].id) {
- memcpy(qc, &(radio_qctrl[i]),
- sizeof(*qc));
+ memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
return 0;
}
}
@@ -396,6 +421,10 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
ctrl->value = radio->muted;
@@ -410,16 +439,22 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value) {
if (amradio_stop(radio) < 0) {
- warn("amradio_stop() failed");
+ amradio_dev_warn(&radio->videodev->dev,
+ "amradio_stop failed\n");
return -1;
}
} else {
if (amradio_start(radio) < 0) {
- warn("amradio_start() failed");
+ amradio_dev_warn(&radio->videodev->dev,
+ "amradio_start failed\n");
return -1;
}
}
@@ -475,30 +510,38 @@ static int usb_amradio_open(struct inode *inode, struct file *file)
radio->muted = 1;
if (amradio_start(radio) < 0) {
- warn("Radio did not start up properly");
+ amradio_dev_warn(&radio->videodev->dev,
+ "radio did not start up properly\n");
radio->users = 0;
unlock_kernel();
return -EIO;
}
if (amradio_setfreq(radio, radio->curfreq) < 0)
- warn("Set frequency failed");
+ amradio_dev_warn(&radio->videodev->dev,
+ "set frequency failed\n");
unlock_kernel();
return 0;
}
-/*close device - free driver structures */
+/*close device */
static int usb_amradio_close(struct inode *inode, struct file *file)
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ int retval;
if (!radio)
return -ENODEV;
+
radio->users = 0;
- if (radio->removed) {
- kfree(radio->buffer);
- kfree(radio);
+
+ if (!radio->removed) {
+ retval = amradio_stop(radio);
+ if (retval < 0)
+ amradio_dev_warn(&radio->videodev->dev,
+ "amradio_stop failed\n");
}
+
return 0;
}
@@ -508,9 +551,9 @@ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
struct amradio_device *radio = usb_get_intfdata(intf);
if (amradio_stop(radio) < 0)
- warn("amradio_stop() failed");
+ dev_warn(&intf->dev, "amradio_stop failed\n");
- info("radio-mr800: Going into suspend..");
+ dev_info(&intf->dev, "going into suspend..\n");
return 0;
}
@@ -521,9 +564,9 @@ static int usb_amradio_resume(struct usb_interface *intf)
struct amradio_device *radio = usb_get_intfdata(intf);
if (amradio_start(radio) < 0)
- warn("amradio_start() failed");
+ dev_warn(&intf->dev, "amradio_start failed\n");
- info("radio-mr800: Coming out of suspend..");
+ dev_info(&intf->dev, "coming out of suspend..\n");
return 0;
}
@@ -555,12 +598,24 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
.vidioc_s_input = vidioc_s_input,
};
+static void usb_amradio_device_release(struct video_device *videodev)
+{
+ struct amradio_device *radio = video_get_drvdata(videodev);
+
+ /* we call v4l to free radio->videodev */
+ video_device_release(videodev);
+
+ /* free rest memory */
+ kfree(radio->buffer);
+ kfree(radio);
+}
+
/* V4L2 interface */
static struct video_device amradio_videodev_template = {
.name = "AverMedia MR 800 USB FM Radio",
.fops = &usb_amradio_fops,
.ioctl_ops = &usb_amradio_ioctl_ops,
- .release = video_device_release,
+ .release = usb_amradio_device_release,
};
/* check if the device is present and register with v4l and
@@ -602,7 +657,7 @@ static int usb_amradio_probe(struct usb_interface *intf,
video_set_drvdata(radio->videodev, radio);
if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) {
- warn("Could not register video device");
+ dev_warn(&intf->dev, "could not register video device\n");
video_device_release(radio->videodev);
kfree(radio->buffer);
kfree(radio);
@@ -617,9 +672,13 @@ static int __init amradio_init(void)
{
int retval = usb_register(&usb_amradio_driver);
- info(DRIVER_VERSION " " DRIVER_DESC);
+ pr_info(KBUILD_MODNAME
+ ": version " DRIVER_VERSION " " DRIVER_DESC "\n");
+
if (retval)
- err("usb_register failed. Error number %d", retval);
+ pr_err(KBUILD_MODNAME
+ ": usb_register failed. Error number %d\n", retval);
+
return retval;
}
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
index a67079777419..7704f243b6f0 100644
--- a/drivers/media/radio/radio-rtrack2.c
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -1,7 +1,7 @@
/* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
*
* Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
- * Converted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
*
* TODO: Allow for more than one of these foolish entities :-)
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index 329c90bddadd..834d43651c70 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -3,7 +3,7 @@
* (c) 1997 M. Kirkwood
* (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
*
- * Fitted to new interface by Alan Cox <alan.cox@linux.org>
+ * Fitted to new interface by Alan Cox <alan@lxorguk.ukuu.org.uk>
* Made working and cleaned up functions <mikael.hedin@irf.se>
* Support for ISAPnP by Ladislav Michl <ladis@psi.cz>
*
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 057fd7e160c4..19cf3b8f67c4 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -184,7 +184,7 @@ config VIDEO_MSP3400
config VIDEO_CS5345
tristate "Cirrus Logic CS5345 audio ADC"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Cirrus Logic CS5345 24-bit, 192 kHz
stereo A/D converter.
@@ -204,7 +204,7 @@ config VIDEO_CS53L32A
config VIDEO_M52790
tristate "Mitsubishi M52790 A/V switch"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Mitsubishi M52790 A/V switch.
@@ -242,7 +242,7 @@ config VIDEO_WM8739
config VIDEO_VP27SMPX
tristate "Panasonic VP27s internal MPX"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the internal MPX of the Panasonic VP27s tuner.
@@ -361,6 +361,17 @@ config VIDEO_SAA7191
To compile this driver as a module, choose M here: the
module will be called saa7191.
+config VIDEO_TVP514X
+ tristate "Texas Instruments TVP514x video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the TI TVP5146/47
+ decoder. It is currently working with the TI OMAP3 camera
+ controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvp514x.
+
config VIDEO_TVP5150
tristate "Texas Instruments TVP5150 video decoder"
depends on VIDEO_V4L2 && I2C
@@ -387,7 +398,7 @@ comment "MPEG video encoders"
config VIDEO_CX2341X
tristate "Conexant CX2341x MPEG encoders"
- depends on VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_COMMON
+ depends on VIDEO_V4L2 && VIDEO_V4L2_COMMON
---help---
Support for the Conexant CX23416 MPEG encoders
and CX23415 MPEG encoder/decoders.
@@ -725,10 +736,16 @@ config MT9M001_PCA9536_SWITCH
extender to switch between 8 and 10 bit datawidth modes
config SOC_CAMERA_MT9M111
- tristate "mt9m111 support"
+ tristate "mt9m111 and mt9m112 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports MT9M111 and MT9M112 cameras from Micron
+
+config SOC_CAMERA_MT9T031
+ tristate "mt9t031 support"
depends on SOC_CAMERA && I2C
help
- This driver supports MT9M111 cameras from Micron
+ This driver supports MT9T031 cameras from Micron.
config SOC_CAMERA_MT9V022
tristate "mt9v022 support"
@@ -744,12 +761,24 @@ config MT9V022_PCA9536_SWITCH
Select this if your MT9V022 camera uses a PCA9536 I2C GPIO
extender to switch between 8 and 10 bit datawidth modes
+config SOC_CAMERA_TW9910
+ tristate "tw9910 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a tw9910 video driver
+
config SOC_CAMERA_PLATFORM
tristate "platform camera support"
depends on SOC_CAMERA
help
This is a generic SoC camera platform driver, useful for testing
+config SOC_CAMERA_OV772X
+ tristate "ov772x camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a ov772x camera driver
+
config VIDEO_PXA27x
tristate "PXA27x Quick Capture Interface driver"
depends on VIDEO_DEV && PXA27x && SOC_CAMERA
@@ -764,6 +793,13 @@ config VIDEO_SH_MOBILE_CEU
---help---
This is a v4l2 driver for the SuperH Mobile CEU Interface
+config VIDEO_OMAP2
+ tristate "OMAP2 Camera Capture Interface driver"
+ depends on VIDEO_DEV && ARCH_OMAP2
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the TI OMAP2 camera capture interface
+
#
# USB Multimedia device configuration
#
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 16962f3aa157..1611c33b1aee 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -8,9 +8,11 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o
stkwebcam-objs := stk-webcam.o stk-sensor.o
-videodev-objs := v4l2-dev.o v4l2-ioctl.o
+omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
-obj-$(CONFIG_VIDEO_DEV) += videodev.o compat_ioctl32.o v4l2-int-device.o
+videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-subdev.o
+
+obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-compat-ioctl32.o v4l2-int-device.o
obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o
@@ -25,6 +27,7 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o
+obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o
@@ -66,6 +69,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
+obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
@@ -129,11 +133,15 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
+obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
+obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
+obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o
obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
+obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o
obj-$(CONFIG_VIDEO_AU0828) += au0828/
diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c
index e09b00693230..2ba6abd92b6f 100644
--- a/drivers/media/video/arv.c
+++ b/drivers/media/video/arv.c
@@ -396,8 +396,7 @@ out_up:
return ret;
}
-static int ar_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int ar_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *dev = video_devdata(file);
struct ar_device *ar = video_get_drvdata(dev);
@@ -543,7 +542,7 @@ static int ar_do_ioctl(struct inode *inode, struct file *file,
static int ar_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, ar_do_ioctl);
+ return video_usercopy(file, cmd, arg, ar_do_ioctl);
}
#if USE_INT
diff --git a/drivers/media/video/bt8xx/bt832.c b/drivers/media/video/bt8xx/bt832.c
deleted file mode 100644
index 216fc9680e80..000000000000
--- a/drivers/media/video/bt8xx/bt832.c
+++ /dev/null
@@ -1,274 +0,0 @@
-/* Driver for Bt832 CMOS Camera Video Processor
- i2c-addresses: 0x88 or 0x8a
-
- The BT832 interfaces to a Quartzsight Digital Camera (352x288, 25 or 30 fps)
- via a 9 pin connector ( 4-wire SDATA, 2-wire i2c, SCLK, VCC, GND).
- It outputs an 8-bit 4:2:2 YUV or YCrCb video signal which can be directly
- connected to bt848/bt878 GPIO pins on this purpose.
- (see: VLSI Vision Ltd. www.vvl.co.uk for camera datasheets)
-
- Supported Cards:
- - Pixelview Rev.4E: 0x8a
- GPIO 0x400000 toggles Bt832 RESET, and the chip changes to i2c 0x88 !
-
- (c) Gunther Mayer, 2002
-
- STATUS:
- - detect chip and hexdump
- - reset chip and leave low power mode
- - detect camera present
-
- TODO:
- - make it work (find correct setup for Bt832 and Bt878)
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/i2c.h>
-#include <linux/types.h>
-#include <linux/videodev.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <media/v4l2-common.h>
-
-#include "bttv.h"
-#include "bt832.h"
-
-MODULE_LICENSE("GPL");
-
-/* Addresses to scan */
-static unsigned short normal_i2c[] = { I2C_ADDR_BT832_ALT1>>1, I2C_ADDR_BT832_ALT2>>1,
- I2C_CLIENT_END };
-I2C_CLIENT_INSMOD;
-
-int debug; /* debug output */
-module_param(debug, int, 0644);
-
-/* ---------------------------------------------------------------------- */
-
-static int bt832_detach(struct i2c_client *client);
-
-
-static struct i2c_driver driver;
-static struct i2c_client client_template;
-
-struct bt832 {
- struct i2c_client client;
-};
-
-int bt832_hexdump(struct i2c_client *i2c_client_s, unsigned char *buf)
-{
- int i,rc;
- buf[0]=0x80; // start at register 0 with auto-increment
- if (1 != (rc = i2c_master_send(i2c_client_s,buf,1)))
- v4l_err(i2c_client_s,"i2c i/o error: rc == %d (should be 1)\n",rc);
-
- for(i=0;i<65;i++)
- buf[i]=0;
- if (65 != (rc=i2c_master_recv(i2c_client_s,buf,65)))
- v4l_err(i2c_client_s,"i2c i/o error: rc == %d (should be 65)\n",rc);
-
- // Note: On READ the first byte is the current index
- // (e.g. 0x80, what we just wrote)
-
- if(debug>1) {
- int i;
- v4l_dbg(2, debug,i2c_client_s,"hexdump:");
- for(i=1;i<65;i++) {
- if(i!=1) {
- if(((i-1)%8)==0) printk(" ");
- if(((i-1)%16)==0) {
- printk("\n");
- v4l_dbg(2, debug,i2c_client_s,"hexdump:");
- }
- }
- printk(" %02x",buf[i]);
- }
- printk("\n");
- }
- return 0;
-}
-
-// Return: 1 (is a bt832), 0 (No bt832 here)
-int bt832_init(struct i2c_client *i2c_client_s)
-{
- unsigned char *buf;
- int rc;
-
- buf=kmalloc(65,GFP_KERNEL);
- if (!buf) {
- v4l_err(&t->client,
- "Unable to allocate memory. Detaching.\n");
- return 0;
- }
- bt832_hexdump(i2c_client_s,buf);
-
- if(buf[0x40] != 0x31) {
- v4l_err(i2c_client_s,"This i2c chip is no bt832 (id=%02x). Detaching.\n",buf[0x40]);
- kfree(buf);
- return 0;
- }
-
- v4l_err(i2c_client_s,"Write 0 tp VPSTATUS\n");
- buf[0]=BT832_VP_STATUS; // Reg.52
- buf[1]= 0x00;
- if (2 != (rc = i2c_master_send(i2c_client_s,buf,2)))
- v4l_err(i2c_client_s,"i2c i/o error VPS: rc == %d (should be 2)\n",rc);
-
- bt832_hexdump(i2c_client_s,buf);
-
-
- // Leave low power mode:
- v4l_err(i2c_client_s,"leave low power mode.\n");
- buf[0]=BT832_CAM_SETUP0; //0x39 57
- buf[1]=0x08;
- if (2 != (rc = i2c_master_send(i2c_client_s,buf,2)))
- v4l_err(i2c_client_s,"i2c i/o error LLPM: rc == %d (should be 2)\n",rc);
-
- bt832_hexdump(i2c_client_s,buf);
-
- v4l_info(i2c_client_s,"Write 0 tp VPSTATUS\n");
- buf[0]=BT832_VP_STATUS; // Reg.52
- buf[1]= 0x00;
- if (2 != (rc = i2c_master_send(i2c_client_s,buf,2)))
- v4l_err(i2c_client_s,"i2c i/o error VPS: rc == %d (should be 2)\n",rc);
-
- bt832_hexdump(i2c_client_s,buf);
-
-
- // Enable Output
- v4l_info(i2c_client_s,"Enable Output\n");
- buf[0]=BT832_VP_CONTROL1; // Reg.40
- buf[1]= 0x27 & (~0x01); // Default | !skip
- if (2 != (rc = i2c_master_send(i2c_client_s,buf,2)))
- v4l_err(i2c_client_s,"i2c i/o error EO: rc == %d (should be 2)\n",rc);
-
- bt832_hexdump(i2c_client_s,buf);
-
-
- // for testing (even works when no camera attached)
- v4l_info(i2c_client_s,"*** Generate NTSC M Bars *****\n");
- buf[0]=BT832_VP_TESTCONTROL0; // Reg. 42
- buf[1]=3; // Generate NTSC System M bars, Generate Frame timing internally
- if (2 != (rc = i2c_master_send(i2c_client_s,buf,2)))
- v4l_info(i2c_client_s,"i2c i/o error MBAR: rc == %d (should be 2)\n",rc);
-
- v4l_info(i2c_client_s,"Camera Present: %s\n",
- (buf[1+BT832_CAM_STATUS] & BT832_56_CAMERA_PRESENT) ? "yes":"no");
-
- bt832_hexdump(i2c_client_s,buf);
- kfree(buf);
- return 1;
-}
-
-
-
-static int bt832_attach(struct i2c_adapter *adap, int addr, int kind)
-{
- struct bt832 *t;
-
- client_template.adapter = adap;
- client_template.addr = addr;
-
- if (NULL == (t = kzalloc(sizeof(*t), GFP_KERNEL)))
- return -ENOMEM;
- t->client = client_template;
- i2c_set_clientdata(&t->client, t);
- i2c_attach_client(&t->client);
-
- v4l_info(&t->client,"chip found @ 0x%x\n", addr<<1);
-
- if(! bt832_init(&t->client)) {
- bt832_detach(&t->client);
- return -1;
- }
-
- return 0;
-}
-
-static int bt832_probe(struct i2c_adapter *adap)
-{
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, bt832_attach);
- return 0;
-}
-
-static int bt832_detach(struct i2c_client *client)
-{
- struct bt832 *t = i2c_get_clientdata(client);
-
- v4l_info(&t->client,"dettach\n");
- i2c_detach_client(client);
- kfree(t);
- return 0;
-}
-
-static int
-bt832_command(struct i2c_client *client, unsigned int cmd, void *arg)
-{
- struct bt832 *t = i2c_get_clientdata(client);
-
- if (debug>1)
- v4l_i2c_print_ioctl(&t->client,cmd);
-
- switch (cmd) {
- case BT832_HEXDUMP: {
- unsigned char *buf;
- buf = kmalloc(65, GFP_KERNEL);
- if (!buf) {
- v4l_err(&t->client,
- "Unable to allocate memory\n");
- break;
- }
- bt832_hexdump(&t->client,buf);
- kfree(buf);
- }
- break;
- case BT832_REATTACH:
- v4l_info(&t->client,"re-attach\n");
- i2c_del_driver(&driver);
- i2c_add_driver(&driver);
- break;
- }
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static struct i2c_driver driver = {
- .driver = {
- .name = "bt832",
- },
- .id = 0, /* FIXME */
- .attach_adapter = bt832_probe,
- .detach_client = bt832_detach,
- .command = bt832_command,
-};
-static struct i2c_client client_template =
-{
- .name = "bt832",
- .driver = &driver,
-};
-
-
-static int __init bt832_init_module(void)
-{
- return i2c_add_driver(&driver);
-}
-
-static void __exit bt832_cleanup_module(void)
-{
- i2c_del_driver(&driver);
-}
-
-module_init(bt832_init_module);
-module_exit(bt832_cleanup_module);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/bt8xx/bt832.h b/drivers/media/video/bt8xx/bt832.h
deleted file mode 100644
index 1ce8fa71f7db..000000000000
--- a/drivers/media/video/bt8xx/bt832.h
+++ /dev/null
@@ -1,305 +0,0 @@
-/* Bt832 CMOS Camera Video Processor (VP)
-
- The Bt832 CMOS Camera Video Processor chip connects a Quartsight CMOS
- color digital camera directly to video capture devices via an 8-bit,
- 4:2:2 YUV or YCrCb video interface.
-
- i2c addresses: 0x88 or 0x8a
- */
-
-/* The 64 registers: */
-
-// Input Processor
-#define BT832_OFFSET 0
-#define BT832_RCOMP 1
-#define BT832_G1COMP 2
-#define BT832_G2COMP 3
-#define BT832_BCOMP 4
-// Exposures:
-#define BT832_FINEH 5
-#define BT832_FINEL 6
-#define BT832_COARSEH 7
-#define BT832_COARSEL 8
-#define BT832_CAMGAIN 9
-// Main Processor:
-#define BT832_M00 10
-#define BT832_M01 11
-#define BT832_M02 12
-#define BT832_M10 13
-#define BT832_M11 14
-#define BT832_M12 15
-#define BT832_M20 16
-#define BT832_M21 17
-#define BT832_M22 18
-#define BT832_APCOR 19
-#define BT832_GAMCOR 20
-// Level Accumulator Inputs
-#define BT832_VPCONTROL2 21
-#define BT832_ZONECODE0 22
-#define BT832_ZONECODE1 23
-#define BT832_ZONECODE2 24
-#define BT832_ZONECODE3 25
-// Level Accumulator Outputs:
-#define BT832_RACC 26
-#define BT832_GACC 27
-#define BT832_BACC 28
-#define BT832_BLACKACC 29
-#define BT832_EXP_AGC 30
-#define BT832_LACC0 31
-#define BT832_LACC1 32
-#define BT832_LACC2 33
-#define BT832_LACC3 34
-#define BT832_LACC4 35
-#define BT832_LACC5 36
-#define BT832_LACC6 37
-#define BT832_LACC7 38
-// System:
-#define BT832_VP_CONTROL0 39
-#define BT832_VP_CONTROL1 40
-#define BT832_THRESH 41
-#define BT832_VP_TESTCONTROL0 42
-#define BT832_VP_DMCODE 43
-#define BT832_ACB_CONFIG 44
-#define BT832_ACB_GNBASE 45
-#define BT832_ACB_MU 46
-#define BT832_CAM_TEST0 47
-#define BT832_AEC_CONFIG 48
-#define BT832_AEC_TL 49
-#define BT832_AEC_TC 50
-#define BT832_AEC_TH 51
-// Status:
-#define BT832_VP_STATUS 52
-#define BT832_VP_LINECOUNT 53
-#define BT832_CAM_DEVICEL 54 // e.g. 0x19
-#define BT832_CAM_DEVICEH 55 // e.g. 0x40 == 0x194 Mask0, 0x194 = 404 decimal (VVL-404 camera)
-#define BT832_CAM_STATUS 56
- #define BT832_56_CAMERA_PRESENT 0x20
-//Camera Setups:
-#define BT832_CAM_SETUP0 57
-#define BT832_CAM_SETUP1 58
-#define BT832_CAM_SETUP2 59
-#define BT832_CAM_SETUP3 60
-// System:
-#define BT832_DEFCOR 61
-#define BT832_VP_TESTCONTROL1 62
-#define BT832_DEVICE_ID 63
-# define BT832_DEVICE_ID__31 0x31 // Bt832 has ID 0x31
-
-/* STMicroelectronivcs VV5404 camera module
- i2c: 0x20: sensor address
- i2c: 0xa0: eeprom for ccd defect map
- */
-#define VV5404_device_h 0x00 // 0x19
-#define VV5404_device_l 0x01 // 0x40
-#define VV5404_status0 0x02
-#define VV5404_linecountc 0x03 // current line counter
-#define VV5404_linecountl 0x04
-#define VV5404_setup0 0x10
-#define VV5404_setup1 0x11
-#define VV5404_setup2 0x12
-#define VV5404_setup4 0x14
-#define VV5404_setup5 0x15
-#define VV5404_fine_h 0x20 // fine exposure
-#define VV5404_fine_l 0x21
-#define VV5404_coarse_h 0x22 //coarse exposure
-#define VV5404_coarse_l 0x23
-#define VV5404_gain 0x24 // ADC pre-amp gain setting
-#define VV5404_clk_div 0x25
-#define VV5404_cr 0x76 // control register
-#define VV5404_as0 0x77 // ADC setup register
-
-
-// IOCTL
-#define BT832_HEXDUMP _IOR('b',1,int)
-#define BT832_REATTACH _IOR('b',2,int)
-
-/* from BT8x8VXD/capdrv/dialogs.cpp */
-
-/*
-typedef enum { SVI, Logitech, Rockwell } CAMERA;
-
-static COMBOBOX_ENTRY gwCameraOptions[] =
-{
- { SVI, "Silicon Vision 512N" },
- { Logitech, "Logitech VideoMan 1.3" },
- { Rockwell, "Rockwell QuartzSight PCI 1.0" }
-};
-
-// SRAM table values
-//===========================================================================
-typedef enum { TGB_NTSC624, TGB_NTSC780, TGB_NTSC858, TGB_NTSC392 } TimeGenByte;
-
-BYTE SRAMTable[][ 60 ] =
-{
- // TGB_NTSC624
- {
- 0x33, // size of table = 51
- 0x0E, 0xC0, 0x00, 0x00, 0x90, 0x02, 0x03, 0x10, 0x03, 0x06,
- 0x10, 0x04, 0x12, 0x12, 0x05, 0x02, 0x13, 0x04, 0x19, 0x00,
- 0x04, 0x39, 0x00, 0x06, 0x59, 0x08, 0x03, 0x85, 0x08, 0x07,
- 0x03, 0x50, 0x00, 0x91, 0x40, 0x00, 0x11, 0x01, 0x01, 0x4D,
- 0x0D, 0x02, 0x03, 0x11, 0x01, 0x05, 0x37, 0x00, 0x37, 0x21, 0x00
- },
- // TGB_NTSC780
- {
- 0x33, // size of table = 51
- 0x0e, 0xc0, 0x00, 0x00, 0x90, 0xe2, 0x03, 0x10, 0x03, 0x06,
- 0x10, 0x34, 0x12, 0x12, 0x65, 0x02, 0x13, 0x24, 0x19, 0x00,
- 0x24, 0x39, 0x00, 0x96, 0x59, 0x08, 0x93, 0x85, 0x08, 0x97,
- 0x03, 0x50, 0x50, 0xaf, 0x40, 0x30, 0x5f, 0x01, 0xf1, 0x7f,
- 0x0d, 0xf2, 0x03, 0x11, 0xf1, 0x05, 0x37, 0x30, 0x85, 0x21, 0x50
- },
- // TGB_NTSC858
- {
- 0x33, // size of table = 51
- 0x0c, 0xc0, 0x00, 0x00, 0x90, 0xc2, 0x03, 0x10, 0x03, 0x06,
- 0x10, 0x34, 0x12, 0x12, 0x65, 0x02, 0x13, 0x24, 0x19, 0x00,
- 0x24, 0x39, 0x00, 0x96, 0x59, 0x08, 0x93, 0x83, 0x08, 0x97,
- 0x03, 0x50, 0x30, 0xc0, 0x40, 0x30, 0x86, 0x01, 0x01, 0xa6,
- 0x0d, 0x62, 0x03, 0x11, 0x61, 0x05, 0x37, 0x30, 0xac, 0x21, 0x50
- },
- // TGB_NTSC392
- // This table has been modified to be used for Fusion Rev D
- {
- 0x2A, // size of table = 42
- 0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24,
- 0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10,
- 0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00,
- 0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3,
- 0x20, 0x00
- }
-};
-
-//===========================================================================
-// This is the structure of the camera specifications
-//===========================================================================
-typedef struct tag_cameraSpec
-{
- SignalFormat signal; // which digital signal format the camera has
- VideoFormat vidFormat; // video standard
- SyncVideoRef syncRef; // which sync video reference is used
- State syncOutput; // enable sync output for sync video input?
- DecInputClk iClk; // which input clock is used
- TimeGenByte tgb; // which timing generator byte does the camera use
- int HReset; // select 64, 48, 32, or 16 CLKx1 for HReset
- PLLFreq pllFreq; // what synthesized frequency to set PLL to
- VSIZEPARMS vSize; // video size the camera produces
- int lineCount; // expected total number of half-line per frame - 1
- BOOL interlace; // interlace signal?
-} CameraSpec;
-
-//===========================================================================
-// <UPDATE REQUIRED>
-// Camera specifications database. Update this table whenever camera spec
-// has been changed or added/deleted supported camera models
-//===========================================================================
-static CameraSpec dbCameraSpec[ N_CAMERAOPTIONS ] =
-{ // Silicon Vision 512N
- { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC624, 64, KHz19636,
- // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace
- { 512, 0x64, 480, 0x13, 240 }, 0, TRUE
- },
- // Logitech VideoMan 1.3
- { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC780, 64, KHz24545,
- // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace
- { 640, 0x80, 480, 0x1A, 240 }, 0, TRUE
- },
- // Rockwell QuartzSight
- // Note: Fusion Rev D (rev ID 0x02) and later supports 16 pixels for HReset which is preferable.
- // Use 32 for earlier version of hardware. Clkx1_HDELAY also changed from 0x27 to 0x20.
- { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC392, 16, KHz28636,
- // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace
- { 352, 0x20, 576, 0x08, 288 }, 607, FALSE
- }
-};
-*/
-
-/*
-The corresponding APIs required to be invoked are:
-SetConnector( ConCamera, TRUE/FALSE );
-SetSignalFormat( spec.signal );
-SetVideoFormat( spec.vidFormat );
-SetSyncVideoRef( spec.syncRef );
-SetEnableSyncOutput( spec.syncOutput );
-SetTimGenByte( SRAMTable[ spec.tgb ], SRAMTableSize[ spec.tgb ] );
-SetHReset( spec.HReset );
-SetPLL( spec.pllFreq );
-SetDecInputClock( spec.iClk );
-SetVideoInfo( spec.vSize );
-SetTotalLineCount( spec.lineCount );
-SetInterlaceMode( spec.interlace );
-*/
-
-/* from web:
- Video Sampling
-Digital video is a sampled form of analog video. The most common sampling schemes in use today are:
- Pixel Clock Horiz Horiz Vert
- Rate Total Active
-NTSC square pixel 12.27 MHz 780 640 525
-NTSC CCIR-601 13.5 MHz 858 720 525
-NTSC 4FSc 14.32 MHz 910 768 525
-PAL square pixel 14.75 MHz 944 768 625
-PAL CCIR-601 13.5 MHz 864 720 625
-PAL 4FSc 17.72 MHz 1135 948 625
-
-For the CCIR-601 standards, the sampling is based on a static orthogonal sampling grid. The luminance component (Y) is sampled at 13.5 MHz, while the two color difference signals, Cr and Cb are sampled at half that, or 6.75 MHz. The Cr and Cb samples are colocated with alternate Y samples, and they are taken at the same position on each line, such that one sample is coincident with the 50% point of the falling edge of analog sync. The samples are coded to either 8 or 10 bits per component.
-*/
-
-/* from DScaler:*/
-/*
-//===========================================================================
-// CCIR656 Digital Input Support: The tables were taken from DScaler proyect
-//
-// 13 Dec 2000 - Michael Eskin, Conexant Systems - Initial version
-//
-
-//===========================================================================
-// Timing generator SRAM table values for CCIR601 720x480 NTSC
-//===========================================================================
-// For NTSC CCIR656
-BYTE BtCard::SRAMTable_NTSC[] =
-{
- // SRAM Timing Table for NTSC
- 0x0c, 0xc0, 0x00,
- 0x00, 0x90, 0xc2,
- 0x03, 0x10, 0x03,
- 0x06, 0x10, 0x34,
- 0x12, 0x12, 0x65,
- 0x02, 0x13, 0x24,
- 0x19, 0x00, 0x24,
- 0x39, 0x00, 0x96,
- 0x59, 0x08, 0x93,
- 0x83, 0x08, 0x97,
- 0x03, 0x50, 0x30,
- 0xc0, 0x40, 0x30,
- 0x86, 0x01, 0x01,
- 0xa6, 0x0d, 0x62,
- 0x03, 0x11, 0x61,
- 0x05, 0x37, 0x30,
- 0xac, 0x21, 0x50
-};
-
-//===========================================================================
-// Timing generator SRAM table values for CCIR601 720x576 NTSC
-//===========================================================================
-// For PAL CCIR656
-BYTE BtCard::SRAMTable_PAL[] =
-{
- // SRAM Timing Table for PAL
- 0x36, 0x11, 0x01,
- 0x00, 0x90, 0x02,
- 0x05, 0x10, 0x04,
- 0x16, 0x14, 0x05,
- 0x11, 0x00, 0x04,
- 0x12, 0xc0, 0x00,
- 0x31, 0x00, 0x06,
- 0x51, 0x08, 0x03,
- 0x89, 0x08, 0x07,
- 0xc0, 0x44, 0x00,
- 0x81, 0x01, 0x01,
- 0xa9, 0x0d, 0x02,
- 0x02, 0x50, 0x03,
- 0x37, 0x3d, 0x00,
- 0xaf, 0x21, 0x00,
-};
-*/
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 13742b0bbe3e..d24dcc025e37 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -44,7 +44,6 @@
/* fwd decl */
static void boot_msp34xx(struct bttv *btv, int pin);
-static void boot_bt832(struct bttv *btv);
static void hauppauge_eeprom(struct bttv *btv);
static void avermedia_eeprom(struct bttv *btv);
static void osprey_eeprom(struct bttv *btv, const u8 ee[256]);
@@ -2217,9 +2216,9 @@ struct tvcard bttv_tvcards[] = {
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
},
- [BTTV_BOARD_VD009X1_MINIDIN] = {
+ [BTTV_BOARD_VD009X1_VD011_MINIDIN] = {
/* M.Klahr@phytec.de */
- .name = "PHYTEC VD-009-X1 MiniDIN (bt878)",
+ .name = "PHYTEC VD-009-X1 VD-011 MiniDIN (bt878)",
.video_inputs = 4,
.audio_inputs = 0,
.tuner = UNSET, /* card has no tuner */
@@ -2227,14 +2226,14 @@ struct tvcard bttv_tvcards[] = {
.gpiomask = 0x00,
.muxsel = { 2, 3, 1, 0 },
.gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
- .needs_tvaudio = 1,
+ .needs_tvaudio = 0,
.pll = PLL_28,
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
},
- [BTTV_BOARD_VD009X1_COMBI] = {
- .name = "PHYTEC VD-009-X1 Combi (bt878)",
+ [BTTV_BOARD_VD009X1_VD011_COMBI] = {
+ .name = "PHYTEC VD-009-X1 VD-011 Combi (bt878)",
.video_inputs = 4,
.audio_inputs = 0,
.tuner = UNSET, /* card has no tuner */
@@ -2242,7 +2241,7 @@ struct tvcard bttv_tvcards[] = {
.gpiomask = 0x00,
.muxsel = { 2, 3, 1, 1 },
.gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
- .needs_tvaudio = 1,
+ .needs_tvaudio = 0,
.pll = PLL_28,
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
@@ -3061,6 +3060,54 @@ struct tvcard bttv_tvcards[] = {
.pll = PLL_28,
.has_radio = 1,
.has_remote = 1,
+ },
+ [BTTV_BOARD_VD012] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012 (bt878)",
+ .video_inputs = 4,
+ .audio_inputs = 0,
+ .tuner = UNSET, /* card has no tuner */
+ .svhs = UNSET, /* card has no s-video */
+ .gpiomask = 0x00,
+ .muxsel = { 0, 2, 3, 1 },
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD012_X1] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012-X1 (bt878)",
+ .video_inputs = 4,
+ .audio_inputs = 0,
+ .tuner = UNSET, /* card has no tuner */
+ .svhs = 3,
+ .gpiomask = 0x00,
+ .muxsel = { 2, 3, 1 },
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD012_X2] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012-X2 (bt878)",
+ .video_inputs = 4,
+ .audio_inputs = 0,
+ .tuner = UNSET, /* card has no tuner */
+ .svhs = 3,
+ .gpiomask = 0x00,
+ .muxsel = { 3, 2, 1 },
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
}
};
@@ -3673,13 +3720,6 @@ void __devinit bttv_init_card2(struct bttv *btv)
if (bttv_tvcards[btv->c.type].audio_mode_gpio)
btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio;
- if (bttv_tvcards[btv->c.type].digital_mode == DIGITAL_MODE_CAMERA) {
- /* detect Bt832 chip for quartzsight digital camera */
- if ((bttv_I2CRead(btv, I2C_ADDR_BT832_ALT1, "Bt832") >=0) ||
- (bttv_I2CRead(btv, I2C_ADDR_BT832_ALT2, "Bt832") >=0))
- boot_bt832(btv);
- }
-
if (!autoload)
return;
@@ -4075,10 +4115,6 @@ static void __devinit boot_msp34xx(struct bttv *btv, int pin)
"init [%d]\n", btv->c.nr, pin);
}
-static void __devinit boot_bt832(struct bttv *btv)
-{
-}
-
/* ----------------------------------------------------------------------- */
/* Imagenation L-Model PXC200 Framegrabber */
/* This is basically the same procedure as
diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/video/bt8xx/bttv-gpio.c
index dce6dae5740e..74c325e594a2 100644
--- a/drivers/media/video/bt8xx/bttv-gpio.c
+++ b/drivers/media/video/bt8xx/bttv-gpio.c
@@ -42,7 +42,7 @@ static int bttv_sub_bus_match(struct device *dev, struct device_driver *drv)
struct bttv_sub_driver *sub = to_bttv_sub_drv(drv);
int len = strlen(sub->wanted);
- if (0 == strncmp(dev->bus_id, sub->wanted, len))
+ if (0 == strncmp(dev_name(dev), sub->wanted, len))
return 1;
return 0;
}
@@ -91,15 +91,14 @@ int bttv_sub_add_device(struct bttv_core *core, char *name)
sub->dev.parent = &core->pci->dev;
sub->dev.bus = &bttv_sub_bus_type;
sub->dev.release = release_sub_device;
- snprintf(sub->dev.bus_id,sizeof(sub->dev.bus_id),"%s%d",
- name, core->nr);
+ dev_set_name(&sub->dev, "%s%d", name, core->nr);
err = device_register(&sub->dev);
if (0 != err) {
kfree(sub);
return err;
}
- printk("bttv%d: add subdevice \"%s\"\n", core->nr, sub->dev.bus_id);
+ printk("bttv%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev));
list_add_tail(&sub->list,&core->subs);
return 0;
}
diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h
index 46cb90e0985b..529bf6cf634d 100644
--- a/drivers/media/video/bt8xx/bttv.h
+++ b/drivers/media/video/bt8xx/bttv.h
@@ -130,8 +130,8 @@
#define BTTV_BOARD_XGUARD 0x67
#define BTTV_BOARD_NEBULA_DIGITV 0x68
#define BTTV_BOARD_PV143 0x69
-#define BTTV_BOARD_VD009X1_MINIDIN 0x6a
-#define BTTV_BOARD_VD009X1_COMBI 0x6b
+#define BTTV_BOARD_VD009X1_VD011_MINIDIN 0x6a
+#define BTTV_BOARD_VD009X1_VD011_COMBI 0x6b
#define BTTV_BOARD_VD009_MINIDIN 0x6c
#define BTTV_BOARD_VD009_COMBI 0x6d
#define BTTV_BOARD_IVC100 0x6e
@@ -177,6 +177,10 @@
#define BTTV_BOARD_GEOVISION_GV600 0x96
#define BTTV_BOARD_KOZUMI_KTV_01C 0x97
#define BTTV_BOARD_ENLTV_FM_2 0x98
+#define BTTV_BOARD_VD012 0x99
+#define BTTV_BOARD_VD012_X1 0x9a
+#define BTTV_BOARD_VD012_X2 0x9b
+
/* more card-specific defines */
#define PT2254_L_CHANNEL 0x10
@@ -308,7 +312,7 @@ struct bttv_sub_device {
struct bttv_sub_driver {
struct device_driver drv;
- char wanted[BUS_ID_SIZE];
+ char wanted[20];
int (*probe)(struct bttv_sub_device *sub);
void (*remove)(struct bttv_sub_device *sub);
};
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
index b4d940b2e447..199a4d225caf 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -459,7 +459,7 @@ struct bttv {
};
/* our devices */
-#define BTTV_MAX 16
+#define BTTV_MAX 32
extern unsigned int bttv_num;
extern struct bttv bttvs[BTTV_MAX];
diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c
index ace4ff9ea023..17f80d03f38e 100644
--- a/drivers/media/video/bw-qcam.c
+++ b/drivers/media/video/bw-qcam.c
@@ -706,8 +706,7 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l
* Video4linux interfacing
*/
-static int qcam_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *dev = video_devdata(file);
struct qcam_device *qcam=(struct qcam_device *)dev;
@@ -867,7 +866,7 @@ static int qcam_do_ioctl(struct inode *inode, struct file *file,
static int qcam_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl);
+ return video_usercopy(file, cmd, arg, qcam_do_ioctl);
}
static ssize_t qcam_read(struct file *file, char __user *buf,
diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c
index 0f930d351466..21c71eb085db 100644
--- a/drivers/media/video/c-qcam.c
+++ b/drivers/media/video/c-qcam.c
@@ -500,8 +500,7 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le
* Video4linux interfacing
*/
-static int qcam_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *dev = video_devdata(file);
struct qcam_device *qcam=(struct qcam_device *)dev;
@@ -667,9 +666,9 @@ static int qcam_do_ioctl(struct inode *inode, struct file *file,
}
static int qcam_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+ unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl);
+ return video_usercopy(file, cmd, arg, qcam_do_ioctl);
}
static ssize_t qcam_read(struct file *file, char __user *buf,
diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c
index 16c094f77852..028a400d2453 100644
--- a/drivers/media/video/cpia.c
+++ b/drivers/media/video/cpia.c
@@ -3333,8 +3333,7 @@ static ssize_t cpia_read(struct file *file, char __user *buf,
return cam->decompressed_frame.count;
}
-static int cpia_do_ioctl(struct inode *inode, struct file *file,
- unsigned int ioctlnr, void *arg)
+static int cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *dev = file->private_data;
struct cam_data *cam = video_get_drvdata(dev);
@@ -3347,9 +3346,9 @@ static int cpia_do_ioctl(struct inode *inode, struct file *file,
if (mutex_lock_interruptible(&cam->busy_lock))
return -EINTR;
- //DBG("cpia_ioctl: %u\n", ioctlnr);
+ /* DBG("cpia_ioctl: %u\n", cmd); */
- switch (ioctlnr) {
+ switch (cmd) {
/* query capabilities */
case VIDIOCGCAP:
{
@@ -3724,7 +3723,7 @@ static int cpia_do_ioctl(struct inode *inode, struct file *file,
static int cpia_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, cpia_do_ioctl);
+ return video_usercopy(file, cmd, arg, cpia_do_ioctl);
}
diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c
index 7e791b6923f9..1cc0df8befff 100644
--- a/drivers/media/video/cpia2/cpia2_core.c
+++ b/drivers/media/video/cpia2/cpia2_core.c
@@ -25,7 +25,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Stripped of 2.4 stuff ready for main kernel submit by
- * Alan Cox <alan@redhat.com>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
*
****************************************************************************/
diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c
index 73511a542077..dc5b07a20f69 100644
--- a/drivers/media/video/cpia2/cpia2_usb.c
+++ b/drivers/media/video/cpia2/cpia2_usb.c
@@ -25,7 +25,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Stripped of 2.4 stuff ready for main kernel submit by
- * Alan Cox <alan@redhat.com>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
****************************************************************************/
#include <linux/kernel.h>
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
index 1c6bd633f193..3c2d7eac1197 100644
--- a/drivers/media/video/cpia2/cpia2_v4l.c
+++ b/drivers/media/video/cpia2/cpia2_v4l.c
@@ -26,7 +26,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Stripped of 2.4 stuff ready for main kernel submit by
- * Alan Cox <alan@redhat.com>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
****************************************************************************/
#include <linux/version.h>
@@ -1572,8 +1572,7 @@ static int ioctl_dqbuf(void *arg,struct camera_data *cam, struct file *file)
* cpia2_ioctl
*
*****************************************************************************/
-static int cpia2_do_ioctl(struct inode *inode, struct file *file,
- unsigned int ioctl_nr, void *arg)
+static int cpia2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct camera_data *cam = video_drvdata(file);
int retval = 0;
@@ -1591,7 +1590,7 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file,
}
/* Priority check */
- switch (ioctl_nr) {
+ switch (cmd) {
case VIDIOCSWIN:
case VIDIOCMCAPTURE:
case VIDIOC_S_FMT:
@@ -1618,7 +1617,7 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file,
break;
}
- switch (ioctl_nr) {
+ switch (cmd) {
case VIDIOCGCAP: /* query capabilities */
retval = ioctl_cap_query(arg, cam);
break;
@@ -1683,7 +1682,7 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file,
case VIDIOC_ENUMINPUT:
case VIDIOC_G_INPUT:
case VIDIOC_S_INPUT:
- retval = ioctl_input(ioctl_nr, arg,cam);
+ retval = ioctl_input(cmd, arg, cam);
break;
case VIDIOC_ENUM_FMT:
@@ -1843,9 +1842,9 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file,
}
static int cpia2_ioctl(struct inode *inode, struct file *file,
- unsigned int ioctl_nr, unsigned long iarg)
+ unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, ioctl_nr, iarg, cpia2_do_ioctl);
+ return video_usercopy(file, cmd, arg, cpia2_do_ioctl);
}
/******************************************************************************
diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c
index a662b15d5b90..70fcd0d5de13 100644
--- a/drivers/media/video/cs5345.c
+++ b/drivers/media/video/cs5345.c
@@ -23,9 +23,9 @@
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-i2c-drv.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC");
MODULE_AUTHOR("Hans Verkuil");
@@ -40,111 +40,143 @@ MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On");
/* ----------------------------------------------------------------------- */
-static inline int cs5345_write(struct i2c_client *client, u8 reg, u8 value)
+static inline int cs5345_write(struct v4l2_subdev *sd, u8 reg, u8 value)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_write_byte_data(client, reg, value);
}
-static inline int cs5345_read(struct i2c_client *client, u8 reg)
+static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_read_byte_data(client, reg);
}
-static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int cs5345_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct v4l2_routing *route = arg;
- struct v4l2_control *ctrl = arg;
-
- switch (cmd) {
- case VIDIOC_INT_G_AUDIO_ROUTING:
- route->input = cs5345_read(client, 0x09) & 7;
- route->input |= cs5345_read(client, 0x05) & 0x70;
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- if ((route->input & 0xf) > 6) {
- v4l_err(client, "Invalid input %d.\n", route->input);
- return -EINVAL;
- }
- cs5345_write(client, 0x09, route->input & 0xf);
- cs5345_write(client, 0x05, route->input & 0xf0);
- break;
-
- case VIDIOC_G_CTRL:
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- ctrl->value = (cs5345_read(client, 0x04) & 0x08) != 0;
- break;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- ctrl->value = cs5345_read(client, 0x07) & 0x3f;
- if (ctrl->value >= 32)
- ctrl->value = ctrl->value - 64;
- break;
-
- case VIDIOC_S_CTRL:
- break;
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- cs5345_write(client, 0x04, ctrl->value ? 0x80 : 0);
- break;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- if (ctrl->value > 24 || ctrl->value < -24)
- return -EINVAL;
- cs5345_write(client, 0x07, ((u8)ctrl->value) & 0x3f);
- cs5345_write(client, 0x08, ((u8)ctrl->value) & 0x3f);
- break;
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client,
- reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = cs5345_read(client, reg->reg & 0x1f);
- else
- cs5345_write(client, reg->reg & 0x1f, reg->val & 0xff);
- break;
+ if ((route->input & 0xf) > 6) {
+ v4l2_err(sd, "Invalid input %d.\n", route->input);
+ return -EINVAL;
}
-#endif
+ cs5345_write(sd, 0x09, route->input & 0xf);
+ cs5345_write(sd, 0x05, route->input & 0xf0);
+ return 0;
+}
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client,
- arg, V4L2_IDENT_CS5345, 0);
-
- case VIDIOC_LOG_STATUS:
- {
- u8 v = cs5345_read(client, 0x09) & 7;
- u8 m = cs5345_read(client, 0x04);
- int vol = cs5345_read(client, 0x08) & 0x3f;
-
- v4l_info(client, "Input: %d%s\n", v,
- (m & 0x80) ? " (muted)" : "");
- if (vol >= 32)
- vol = vol - 64;
- v4l_info(client, "Volume: %d dB\n", vol);
- break;
- }
-
- default:
+static int cs5345_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl->value = (cs5345_read(sd, 0x04) & 0x08) != 0;
+ return 0;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
return -EINVAL;
+ ctrl->value = cs5345_read(sd, 0x07) & 0x3f;
+ if (ctrl->value >= 32)
+ ctrl->value = ctrl->value - 64;
+ return 0;
+}
+
+static int cs5345_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ cs5345_write(sd, 0x04, ctrl->value ? 0x80 : 0);
+ return 0;
}
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ if (ctrl->value > 24 || ctrl->value < -24)
+ return -EINVAL;
+ cs5345_write(sd, 0x07, ((u8)ctrl->value) & 0x3f);
+ cs5345_write(sd, 0x08, ((u8)ctrl->value) & 0x3f);
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = cs5345_read(sd, reg->reg & 0x1f);
+ return 0;
+}
+
+static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cs5345_write(sd, reg->reg & 0x1f, reg->val & 0xff);
return 0;
}
+#endif
+
+static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_CS5345, 0);
+}
+
+static int cs5345_log_status(struct v4l2_subdev *sd)
+{
+ u8 v = cs5345_read(sd, 0x09) & 7;
+ u8 m = cs5345_read(sd, 0x04);
+ int vol = cs5345_read(sd, 0x08) & 0x3f;
+
+ v4l2_info(sd, "Input: %d%s\n", v,
+ (m & 0x80) ? " (muted)" : "");
+ if (vol >= 32)
+ vol = vol - 64;
+ v4l2_info(sd, "Volume: %d dB\n", vol);
+ return 0;
+}
+
+static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops cs5345_core_ops = {
+ .log_status = cs5345_log_status,
+ .g_chip_ident = cs5345_g_chip_ident,
+ .g_ctrl = cs5345_g_ctrl,
+ .s_ctrl = cs5345_s_ctrl,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cs5345_g_register,
+ .s_register = cs5345_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_audio_ops cs5345_audio_ops = {
+ .s_routing = cs5345_s_routing,
+};
+
+static const struct v4l2_subdev_ops cs5345_ops = {
+ .core = &cs5345_core_ops,
+ .audio = &cs5345_audio_ops,
+};
/* ----------------------------------------------------------------------- */
static int cs5345_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
+
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
@@ -152,9 +184,25 @@ static int cs5345_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- cs5345_write(client, 0x02, 0x00);
- cs5345_write(client, 0x04, 0x01);
- cs5345_write(client, 0x09, 0x01);
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &cs5345_ops);
+
+ cs5345_write(sd, 0x02, 0x00);
+ cs5345_write(sd, 0x04, 0x01);
+ cs5345_write(sd, 0x09, 0x01);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int cs5345_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
return 0;
}
@@ -171,5 +219,6 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.driverid = I2C_DRIVERID_CS5345,
.command = cs5345_command,
.probe = cs5345_probe,
+ .remove = cs5345_remove,
.id_table = cs5345_id,
};
diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c
index c4444500b330..cb65d519cf78 100644
--- a/drivers/media/video/cs53l32a.c
+++ b/drivers/media/video/cs53l32a.c
@@ -27,7 +27,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv-legacy.h>
@@ -47,84 +47,104 @@ I2C_CLIENT_INSMOD;
/* ----------------------------------------------------------------------- */
-static int cs53l32a_write(struct i2c_client *client, u8 reg, u8 value)
+static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_write_byte_data(client, reg, value);
}
-static int cs53l32a_read(struct i2c_client *client, u8 reg)
+static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_read_byte_data(client, reg);
}
-static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int cs53l32a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct v4l2_routing *route = arg;
- struct v4l2_control *ctrl = arg;
-
- switch (cmd) {
- case VIDIOC_INT_G_AUDIO_ROUTING:
- route->input = (cs53l32a_read(client, 0x01) >> 4) & 3;
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- /* There are 2 physical inputs, but the second input can be
- placed in two modes, the first mode bypasses the PGA (gain),
- the second goes through the PGA. Hence there are three
- possible inputs to choose from. */
- if (route->input > 2) {
- v4l_err(client, "Invalid input %d.\n", route->input);
- return -EINVAL;
- }
- cs53l32a_write(client, 0x01, 0x01 + (route->input << 4));
- break;
-
- case VIDIOC_G_CTRL:
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- ctrl->value = (cs53l32a_read(client, 0x03) & 0xc0) != 0;
- break;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- ctrl->value = (s8)cs53l32a_read(client, 0x04);
- break;
-
- case VIDIOC_S_CTRL:
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- cs53l32a_write(client, 0x03, ctrl->value ? 0xf0 : 0x30);
- break;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- if (ctrl->value > 12 || ctrl->value < -96)
- return -EINVAL;
- cs53l32a_write(client, 0x04, (u8) ctrl->value);
- cs53l32a_write(client, 0x05, (u8) ctrl->value);
- break;
-
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client,
- arg, V4L2_IDENT_CS53l32A, 0);
-
- case VIDIOC_LOG_STATUS:
- {
- u8 v = cs53l32a_read(client, 0x01);
- u8 m = cs53l32a_read(client, 0x03);
- s8 vol = cs53l32a_read(client, 0x04);
-
- v4l_info(client, "Input: %d%s\n", (v >> 4) & 3,
- (m & 0xC0) ? " (muted)" : "");
- v4l_info(client, "Volume: %d dB\n", vol);
- break;
- }
-
- default:
+ /* There are 2 physical inputs, but the second input can be
+ placed in two modes, the first mode bypasses the PGA (gain),
+ the second goes through the PGA. Hence there are three
+ possible inputs to choose from. */
+ if (route->input > 2) {
+ v4l2_err(sd, "Invalid input %d.\n", route->input);
return -EINVAL;
}
+ cs53l32a_write(sd, 0x01, 0x01 + (route->input << 4));
+ return 0;
+}
+
+static int cs53l32a_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl->value = (cs53l32a_read(sd, 0x03) & 0xc0) != 0;
+ return 0;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ ctrl->value = (s8)cs53l32a_read(sd, 0x04);
+ return 0;
+}
+
+static int cs53l32a_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ cs53l32a_write(sd, 0x03, ctrl->value ? 0xf0 : 0x30);
+ return 0;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ if (ctrl->value > 12 || ctrl->value < -96)
+ return -EINVAL;
+ cs53l32a_write(sd, 0x04, (u8) ctrl->value);
+ cs53l32a_write(sd, 0x05, (u8) ctrl->value);
return 0;
}
+static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client,
+ chip, V4L2_IDENT_CS53l32A, 0);
+}
+
+static int cs53l32a_log_status(struct v4l2_subdev *sd)
+{
+ u8 v = cs53l32a_read(sd, 0x01);
+ u8 m = cs53l32a_read(sd, 0x03);
+ s8 vol = cs53l32a_read(sd, 0x04);
+
+ v4l2_info(sd, "Input: %d%s\n", (v >> 4) & 3,
+ (m & 0xC0) ? " (muted)" : "");
+ v4l2_info(sd, "Volume: %d dB\n", vol);
+ return 0;
+}
+
+static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
+ .log_status = cs53l32a_log_status,
+ .g_chip_ident = cs53l32a_g_chip_ident,
+ .g_ctrl = cs53l32a_g_ctrl,
+ .s_ctrl = cs53l32a_s_ctrl,
+};
+
+static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
+ .s_routing = cs53l32a_s_routing,
+};
+
+static const struct v4l2_subdev_ops cs53l32a_ops = {
+ .core = &cs53l32a_core_ops,
+ .audio = &cs53l32a_audio_ops,
+};
+
/* ----------------------------------------------------------------------- */
/* i2c implementation */
@@ -137,6 +157,7 @@ static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg)
static int cs53l32a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
int i;
/* Check if the adapter supports the needed features */
@@ -149,32 +170,46 @@ static int cs53l32a_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
+ sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops);
+
for (i = 1; i <= 7; i++) {
- u8 v = cs53l32a_read(client, i);
+ u8 v = cs53l32a_read(sd, i);
- v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v);
+ v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
}
/* Set cs53l32a internal register for Adaptec 2010/2410 setup */
- cs53l32a_write(client, 0x01, (u8) 0x21);
- cs53l32a_write(client, 0x02, (u8) 0x29);
- cs53l32a_write(client, 0x03, (u8) 0x30);
- cs53l32a_write(client, 0x04, (u8) 0x00);
- cs53l32a_write(client, 0x05, (u8) 0x00);
- cs53l32a_write(client, 0x06, (u8) 0x00);
- cs53l32a_write(client, 0x07, (u8) 0x00);
+ cs53l32a_write(sd, 0x01, (u8) 0x21);
+ cs53l32a_write(sd, 0x02, (u8) 0x29);
+ cs53l32a_write(sd, 0x03, (u8) 0x30);
+ cs53l32a_write(sd, 0x04, (u8) 0x00);
+ cs53l32a_write(sd, 0x05, (u8) 0x00);
+ cs53l32a_write(sd, 0x06, (u8) 0x00);
+ cs53l32a_write(sd, 0x07, (u8) 0x00);
/* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
for (i = 1; i <= 7; i++) {
- u8 v = cs53l32a_read(client, i);
+ u8 v = cs53l32a_read(sd, i);
- v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v);
+ v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
}
return 0;
}
+static int cs53l32a_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ return 0;
+}
+
static const struct i2c_device_id cs53l32a_id[] = {
{ "cs53l32a", 0 },
{ }
@@ -185,6 +220,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "cs53l32a",
.driverid = I2C_DRIVERID_CS53L32A,
.command = cs53l32a_command,
+ .remove = cs53l32a_remove,
.probe = cs53l32a_probe,
.id_table = cs53l32a_id,
};
diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c
index 0b55837880a7..a2f0ad570434 100644
--- a/drivers/media/video/cx18/cx18-av-audio.c
+++ b/drivers/media/video/cx18/cx18-av-audio.c
@@ -4,6 +4,7 @@
* Derived from cx25840-audio.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -30,98 +31,165 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
if (freq != 32000 && freq != 44100 && freq != 48000)
return -EINVAL;
- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
- cx18_av_write(cx, 0x127, 0x50);
+ /*
+ * The PLL parameters are based on the external crystal frequency that
+ * would ideally be:
+ *
+ * NTSC Color subcarrier freq * 8 =
+ * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+ *
+ * The accidents of history and rationale that explain from where this
+ * combination of magic numbers originate can be found in:
+ *
+ * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
+ * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
+ *
+ * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
+ * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
+ *
+ * As Mike Bradley has rightly pointed out, it's not the exact crystal
+ * frequency that matters, only that all parts of the driver and
+ * firmware are using the same value (close to the ideal value).
+ *
+ * Since I have a strong suspicion that, if the firmware ever assumes a
+ * crystal value at all, it will assume 28.636360 MHz, the crystal
+ * freq used in calculations in this driver will be:
+ *
+ * xtal_freq = 28.636360 MHz
+ *
+ * an error of less than 0.13 ppm which is way, way better than any off
+ * the shelf crystal will have for accuracy anyway.
+ *
+ * Below I aim to run the PLLs' VCOs near 400 MHz to minimze error.
+ *
+ * Many thanks to Jeff Campbell and Mike Bradley for their extensive
+ * investigation, experimentation, testing, and suggested solutions of
+ * of audio/video sync problems with SVideo and CVBS captures.
+ */
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
switch (freq) {
case 32000:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x1408040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20
+ */
+ cx18_av_write4(cx, 0x108, 0x200d040f);
- /* AUX_PLL_FRAC */
- /* 0x8.9504318a * 28,636,363.636 / 0x14 = 32000 * 384 */
- cx18_av_write4(cx, 0x110, 0x012a0863);
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x20 = 32000 * 384: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
/* src3/4/6_ctl */
- /* 0x1.f77f = (4 * 15734.26) / 32000 */
+ /* 0x1.f77f = (4 * xtal/8*2/455) / 32000 */
cx18_av_write4(cx, 0x900, 0x0801f77f);
cx18_av_write4(cx, 0x904, 0x0801f77f);
cx18_av_write4(cx, 0x90c, 0x0801f77f);
- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
- cx18_av_write(cx, 0x127, 0x54);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */
+ cx18_av_write(cx, 0x127, 0x60);
/* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */
cx18_av_write4(cx, 0x12c, 0x11202fff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x0d2ef8 = 107999.000 * 8 =
* ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa10d2ef8);
+ cx18_av_write4(cx, 0x128, 0xa00d2ef8);
break;
case 44100:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x1009040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x18
+ */
+ cx18_av_write4(cx, 0x108, 0x180e040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
- /* AUX_PLL_FRAC */
- /* 0x9.7635e7 * 28,636,363.63 / 0x10 = 44100 * 384 */
- cx18_av_write4(cx, 0x110, 0x00ec6bce);
+ /* AUX_PLL Fraction = 0x062a1f2 */
+ /* xtal * 0xe.3150f90/0x18 = 44100 * 384: 406 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0062a1f2);
/* src3/4/6_ctl */
- /* 0x1.6d59 = (4 * 15734.26) / 44100 */
+ /* 0x1.6d59 = (4 * xtal/8*2/455) / 44100 */
cx18_av_write4(cx, 0x900, 0x08016d59);
cx18_av_write4(cx, 0x904, 0x08016d59);
cx18_av_write4(cx, 0x90c, 0x08016d59);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x18 */
+ cx18_av_write(cx, 0x127, 0x58);
+
/* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */
cx18_av_write4(cx, 0x12c, 0x112092ff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x1d4bf8 = 239999.000 * 8 =
* ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa11d4bf8);
+ cx18_av_write4(cx, 0x128, 0xa01d4bf8);
break;
case 48000:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x100a040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x16
+ */
+ cx18_av_write4(cx, 0x108, 0x160e040f);
- /* AUX_PLL_FRAC */
- /* 0xa.4c6b6ea * 28,636,363.63 / 0x10 = 48000 * 384 */
- cx18_av_write4(cx, 0x110, 0x0098d6dd);
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x05227ad */
+ /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x005227ad);
/* src3/4/6_ctl */
- /* 0x1.4faa = (4 * 15734.26) / 48000 */
+ /* 0x1.4faa = (4 * xtal/8*2/455) / 48000 */
cx18_av_write4(cx, 0x900, 0x08014faa);
cx18_av_write4(cx, 0x904, 0x08014faa);
cx18_av_write4(cx, 0x90c, 0x08014faa);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+ cx18_av_write(cx, 0x127, 0x56);
+
/* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */
cx18_av_write4(cx, 0x12c, 0x11205fff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x1193f8 = 143999.000 * 8 =
* ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa11193f8);
+ cx18_av_write4(cx, 0x128, 0xa01193f8);
break;
}
} else {
switch (freq) {
case 32000:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x1e08040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x30
+ */
+ cx18_av_write4(cx, 0x108, 0x300d040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
- /* AUX_PLL_FRAC */
- /* 0x8.9504318 * 28,636,363.63 / 0x1e = 32000 * 256 */
- cx18_av_write4(cx, 0x110, 0x012a0863);
+ /* AUX_PLL Fraction = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x30 = 32000 * 256: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
/* src1_ctl */
/* 0x1.0000 = 32000/32000 */
@@ -133,27 +201,34 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
cx18_av_write4(cx, 0x904, 0x08020000);
cx18_av_write4(cx, 0x90c, 0x08020000);
- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
- cx18_av_write(cx, 0x127, 0x54);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x30 */
+ cx18_av_write(cx, 0x127, 0x70);
/* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */
cx18_av_write4(cx, 0x12c, 0x11201fff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x0d2ef8 = 107999.000 * 8 =
* ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa10d2ef8);
+ cx18_av_write4(cx, 0x128, 0xa00d2ef8);
break;
case 44100:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x1809040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x24
+ */
+ cx18_av_write4(cx, 0x108, 0x240e040f);
- /* AUX_PLL_FRAC */
- /* 0x9.7635e74 * 28,636,363.63 / 0x18 = 44100 * 256 */
- cx18_av_write4(cx, 0x110, 0x00ec6bce);
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x062a1f2 */
+ /* xtal * 0xe.3150f90/0x24 = 44100 * 256: 406 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0062a1f2);
/* src1_ctl */
/* 0x1.60cd = 44100/32000 */
@@ -165,24 +240,34 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
cx18_av_write4(cx, 0x904, 0x08017385);
cx18_av_write4(cx, 0x90c, 0x08017385);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x24 */
+ cx18_av_write(cx, 0x127, 0x64);
+
/* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */
cx18_av_write4(cx, 0x12c, 0x112061ff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x1d4bf8 = 239999.000 * 8 =
* ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa11d4bf8);
+ cx18_av_write4(cx, 0x128, 0xa01d4bf8);
break;
case 48000:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x180a040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20
+ */
+ cx18_av_write4(cx, 0x108, 0x200d040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
- /* AUX_PLL_FRAC */
- /* 0xa.4c6b6ea * 28,636,363.63 / 0x18 = 48000 * 256 */
- cx18_av_write4(cx, 0x110, 0x0098d6dd);
+ /* AUX_PLL Fraction = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x20 = 48000 * 256: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
/* src1_ctl */
/* 0x1.8000 = 48000/32000 */
@@ -194,15 +279,18 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
cx18_av_write4(cx, 0x904, 0x08015555);
cx18_av_write4(cx, 0x90c, 0x08015555);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */
+ cx18_av_write(cx, 0x127, 0x60);
+
/* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */
cx18_av_write4(cx, 0x12c, 0x11203fff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x1193f8 = 143999.000 * 8 =
* ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa11193f8);
+ cx18_av_write4(cx, 0x128, 0xa01193f8);
break;
}
}
@@ -215,12 +303,15 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
void cx18_av_audio_set_path(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
+ u8 v;
/* stop microcontroller */
- cx18_av_and_or(cx, 0x803, ~0x10, 0);
+ v = cx18_av_read(cx, 0x803) & ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
/* assert soft reset */
- cx18_av_and_or(cx, 0x810, ~0x1, 0x01);
+ v = cx18_av_read(cx, 0x810) | 0x01;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
/* Mute everything to prevent the PFFT! */
cx18_av_write(cx, 0x8d3, 0x1f);
@@ -240,12 +331,14 @@ void cx18_av_audio_set_path(struct cx18 *cx)
set_audclk_freq(cx, state->audclk_freq);
/* deassert soft reset */
- cx18_av_and_or(cx, 0x810, ~0x1, 0x00);
+ v = cx18_av_read(cx, 0x810) & ~0x01;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
/* When the microcontroller detects the
* audio format, it will unmute the lines */
- cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ v = cx18_av_read(cx, 0x803) | 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
}
@@ -347,19 +440,23 @@ static int get_mute(struct cx18 *cx)
static void set_mute(struct cx18 *cx, int mute)
{
struct cx18_av_state *state = &cx->av_state;
+ u8 v;
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
/* Must turn off microcontroller in order to mute sound.
* Not sure if this is the best method, but it does work.
* If the microcontroller is running, then it will undo any
* changes to the mute register. */
+ v = cx18_av_read(cx, 0x803);
if (mute) {
/* disable microcontroller */
- cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
+ v &= ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
cx18_av_write(cx, 0x8d3, 0x1f);
} else {
/* enable microcontroller */
- cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ v |= 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
} else {
/* SRC1_MUTE_EN */
@@ -375,16 +472,26 @@ int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
switch (cmd) {
case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+ {
+ u8 v;
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
- cx18_av_and_or(cx, 0x803, ~0x10, 0);
+ v = cx18_av_read(cx, 0x803) & ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
cx18_av_write(cx, 0x8d3, 0x1f);
}
- cx18_av_and_or(cx, 0x810, ~0x1, 1);
+ v = cx18_av_read(cx, 0x810) | 0x1;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+
retval = set_audclk_freq(cx, *(u32 *)arg);
- cx18_av_and_or(cx, 0x810, ~0x1, 0);
- if (state->aud_input > CX18_AV_AUDIO_SERIAL2)
- cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+
+ v = cx18_av_read(cx, 0x810) & ~0x1;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+ v = cx18_av_read(cx, 0x803) | 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ }
return retval;
+ }
case VIDIOC_G_CTRL:
switch (ctrl->id) {
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
index 73f5141a42d1..0b1c84b4ddd6 100644
--- a/drivers/media/video/cx18/cx18-av-core.c
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -4,6 +4,7 @@
* Derived from cx25840-core.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -36,12 +37,31 @@ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
return 0;
}
+int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask)
+{
+ u32 reg = 0xc40000 + (addr & ~3);
+ int shift = (addr & 3) * 8;
+ u32 x = cx18_read_reg(cx, reg);
+
+ x = (x & ~((u32)0xff << shift)) | ((u32)value << shift);
+ cx18_write_reg_expect(cx, x, reg,
+ ((u32)eval << shift), ((u32)mask << shift));
+ return 0;
+}
+
int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value)
{
cx18_write_reg(cx, value, 0xc40000 + addr);
return 0;
}
+int
+cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval, u32 mask)
+{
+ cx18_write_reg_expect(cx, value, 0xc40000 + addr, eval, mask);
+ return 0;
+}
+
int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value)
{
cx18_write_reg_noretry(cx, value, 0xc40000 + addr);
@@ -61,11 +81,6 @@ u32 cx18_av_read4(struct cx18 *cx, u16 addr)
return cx18_read_reg(cx, 0xc40000 + addr);
}
-u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr)
-{
- return cx18_read_reg_noretry(cx, 0xc40000 + addr);
-}
-
int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask,
u8 or_value)
{
@@ -98,14 +113,16 @@ static void cx18_av_initialize(struct cx18 *cx)
cx18_av_loadfw(cx);
/* Stop 8051 code execution */
- cx18_av_write4(cx, CXADEC_DL_CTL, 0x03000000);
+ cx18_av_write4_expect(cx, CXADEC_DL_CTL, 0x03000000,
+ 0x03000000, 0x13000000);
/* initallize the PLL by toggling sleep bit */
v = cx18_av_read4(cx, CXADEC_HOST_REG1);
- /* enable sleep mode */
- cx18_av_write4(cx, CXADEC_HOST_REG1, v | 1);
+ /* enable sleep mode - register appears to be read only... */
+ cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v | 1, v, 0xfffe);
/* disable sleep mode */
- cx18_av_write4(cx, CXADEC_HOST_REG1, v & 0xfffe);
+ cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v & 0xfffe,
+ v & 0xfffe, 0xffff);
/* initialize DLLs */
v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF;
@@ -125,9 +142,10 @@ static void cx18_av_initialize(struct cx18 *cx)
v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1;
/* enable TUNE_FIL_RST */
- cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v);
+ cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3, v, v, 0x03009F0F);
/* disable TUNE_FIL_RST */
- cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v & 0xFFFFFFFE);
+ cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3,
+ v & 0xFFFFFFFE, v & 0xFFFFFFFE, 0x03009F0F);
/* enable 656 output */
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00);
@@ -251,10 +269,9 @@ void cx18_av_std_setup(struct cx18 *cx)
pll_int, pll_frac, pll_post);
if (pll_post) {
- int fin, fsc;
- int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac);
+ int fin, fsc, pll;
- pll >>= 25;
+ pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25;
pll /= pll_post;
CX18_DEBUG_INFO("PLL = %d.%06d MHz\n",
pll / 1000000, pll % 1000000);
@@ -324,6 +341,7 @@ static void input_change(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
v4l2_std_id std = state->std;
+ u8 v;
/* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */
cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
@@ -333,31 +351,34 @@ static void input_change(struct cx18 *cx)
if (std & V4L2_STD_525_60) {
if (std == V4L2_STD_NTSC_M_JP) {
/* Japan uses EIAJ audio standard */
- cx18_av_write(cx, 0x808, 0xf7);
- cx18_av_write(cx, 0x80b, 0x02);
+ cx18_av_write_expect(cx, 0x808, 0xf7, 0xf7, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x02, 0x02, 0x3f);
} else if (std == V4L2_STD_NTSC_M_KR) {
/* South Korea uses A2 audio standard */
- cx18_av_write(cx, 0x808, 0xf8);
- cx18_av_write(cx, 0x80b, 0x03);
+ cx18_av_write_expect(cx, 0x808, 0xf8, 0xf8, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
} else {
/* Others use the BTSC audio standard */
- cx18_av_write(cx, 0x808, 0xf6);
- cx18_av_write(cx, 0x80b, 0x01);
+ cx18_av_write_expect(cx, 0x808, 0xf6, 0xf6, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x01, 0x01, 0x3f);
}
} else if (std & V4L2_STD_PAL) {
/* Follow tuner change procedure for PAL */
- cx18_av_write(cx, 0x808, 0xff);
- cx18_av_write(cx, 0x80b, 0x03);
+ cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
} else if (std & V4L2_STD_SECAM) {
/* Select autodetect for SECAM */
- cx18_av_write(cx, 0x808, 0xff);
- cx18_av_write(cx, 0x80b, 0x03);
+ cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
}
- if (cx18_av_read(cx, 0x803) & 0x10) {
+ v = cx18_av_read(cx, 0x803);
+ if (v & 0x10) {
/* restart audio decoder microcontroller */
- cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
- cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ v &= ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ v |= 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
}
@@ -368,6 +389,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 &&
vid_input <= CX18_AV_COMPOSITE8);
u8 reg;
+ u8 v;
CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n",
vid_input, aud_input);
@@ -413,16 +435,23 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
return -EINVAL;
}
- cx18_av_write(cx, 0x103, reg);
+ cx18_av_write_expect(cx, 0x103, reg, reg, 0xf7);
/* Set INPUT_MODE to Composite (0) or S-Video (1) */
cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02);
+
/* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
- cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
+ v = cx18_av_read(cx, 0x102);
+ if (reg & 0x80)
+ v &= ~0x2;
+ else
+ v |= 0x2;
/* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
- cx18_av_and_or(cx, 0x102, ~0x4, 4);
+ v |= 0x4;
else
- cx18_av_and_or(cx, 0x102, ~0x4, 0);
+ v &= ~0x4;
+ cx18_av_write_expect(cx, 0x102, v, v, 0x17);
+
/*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/
state->vid_input = vid_input;
@@ -799,40 +828,47 @@ int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg)
}
case VIDIOC_S_TUNER:
+ {
+ u8 v;
+
if (state->radio)
break;
+ v = cx18_av_read(cx, 0x809);
+ v &= ~0xf;
+
switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO:
/* mono -> mono
stereo -> mono
bilingual -> lang1 */
- cx18_av_and_or(cx, 0x809, ~0xf, 0x00);
break;
case V4L2_TUNER_MODE_STEREO:
case V4L2_TUNER_MODE_LANG1:
/* mono -> mono
stereo -> stereo
bilingual -> lang1 */
- cx18_av_and_or(cx, 0x809, ~0xf, 0x04);
+ v |= 0x4;
break;
case V4L2_TUNER_MODE_LANG1_LANG2:
/* mono -> mono
stereo -> stereo
bilingual -> lang1/lang2 */
- cx18_av_and_or(cx, 0x809, ~0xf, 0x07);
+ v |= 0x7;
break;
case V4L2_TUNER_MODE_LANG2:
/* mono -> mono
stereo -> stereo
bilingual -> lang2 */
- cx18_av_and_or(cx, 0x809, ~0xf, 0x01);
+ v |= 0x1;
break;
default:
return -EINVAL;
}
+ cx18_av_write_expect(cx, 0x809, v, v, 0xff);
state->audmode = vt->audmode;
break;
+ }
case VIDIOC_G_FMT:
return get_v4lfmt(cx, (struct v4l2_format *)arg);
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
index b67d8df20cc6..cf68a6039091 100644
--- a/drivers/media/video/cx18/cx18-av-core.h
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -4,6 +4,7 @@
* Derived from cx25840-core.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -302,9 +303,11 @@ struct cx18_av_state {
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value);
int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value);
+int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask);
+int cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval,
+ u32 mask);
u8 cx18_av_read(struct cx18 *cx, u16 addr);
u32 cx18_av_read4(struct cx18 *cx, u16 addr);
-u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr);
int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg);
diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c
index 522a035b2e8f..c64fd0a05a97 100644
--- a/drivers/media/video/cx18/cx18-av-firmware.c
+++ b/drivers/media/video/cx18/cx18-av-firmware.c
@@ -2,6 +2,7 @@
* cx18 ADEC firmware functions
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -43,11 +44,13 @@ int cx18_av_loadfw(struct cx18 *cx)
/* The firmware load often has byte errors, so allow for several
retries, both at byte level and at the firmware load level. */
while (retries1 < 5) {
- cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000);
- cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6);
+ cx18_av_write4_expect(cx, CXADEC_CHIP_CTRL, 0x00010000,
+ 0x00008430, 0xffffffff); /* cx25843 */
+ cx18_av_write_expect(cx, CXADEC_STD_DET_CTL, 0xf6, 0xf6, 0xff);
- /* Reset the Mako core (Register is undocumented.) */
- cx18_av_write4(cx, 0x8100, 0x00010000);
+ /* Reset the Mako core, Register is alias of CXADEC_CHIP_CTRL */
+ cx18_av_write4_expect(cx, 0x8100, 0x00010000,
+ 0x00008430, 0xffffffff); /* cx25843 */
/* Put the 8051 in reset and enable firmware upload */
cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000);
@@ -61,13 +64,12 @@ int cx18_av_loadfw(struct cx18 *cx)
int retries2;
int unrec_err = 0;
- for (retries2 = 0; retries2 < CX18_MAX_MMIO_RETRIES;
+ for (retries2 = 0; retries2 < CX18_MAX_MMIO_WR_RETRIES;
retries2++) {
cx18_av_write4_noretry(cx, CXADEC_DL_CTL,
dl_control);
udelay(10);
- value = cx18_av_read4_noretry(cx,
- CXADEC_DL_CTL);
+ value = cx18_av_read4(cx, CXADEC_DL_CTL);
if (value == dl_control)
break;
/* Check if we can correct the byte by changing
@@ -78,9 +80,7 @@ int cx18_av_loadfw(struct cx18 *cx)
break;
}
}
- cx18_log_write_retries(cx, retries2,
- cx->reg_mem + 0xc40000 + CXADEC_DL_CTL);
- if (unrec_err || retries2 >= CX18_MAX_MMIO_RETRIES)
+ if (unrec_err || retries2 >= CX18_MAX_MMIO_WR_RETRIES)
break;
}
if (i == size)
@@ -93,7 +93,8 @@ int cx18_av_loadfw(struct cx18 *cx)
return -EIO;
}
- cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size);
+ cx18_av_write4_expect(cx, CXADEC_DL_CTL,
+ 0x13000000 | fw->size, 0x13000000, 0x13000000);
/* Output to the 416 */
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
@@ -118,7 +119,8 @@ int cx18_av_loadfw(struct cx18 *cx)
passthrough */
cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687);
- cx18_av_write4(cx, CXADEC_STD_DET_CTL, 0x000000F6);
+ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6,
+ 0x3F00FFFF);
/* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */
/* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
@@ -136,7 +138,7 @@ int cx18_av_loadfw(struct cx18 *cx)
v |= 0xFF; /* Auto by default */
v |= 0x400; /* Stereo by default */
v |= 0x14000000;
- cx18_av_write4(cx, CXADEC_STD_DET_CTL, v);
+ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF);
release_firmware(fw);
diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c
index 02fdf57bb678..1527ea4f6b06 100644
--- a/drivers/media/video/cx18/cx18-av-vbi.c
+++ b/drivers/media/video/cx18/cx18-av-vbi.c
@@ -141,10 +141,11 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
u8 lcr[24];
fmt = arg;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE &&
+ fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE)
return -EINVAL;
svbi = &fmt->fmt.sliced;
- if (svbi->service_set == 0) {
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
/* raw VBI */
memset(svbi, 0, sizeof(*svbi));
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
index 5efe01ebe9db..e274043657dd 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -4,6 +4,7 @@
* Derived from ivtv-cards.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -50,7 +51,7 @@ static struct cx18_card_tuner_i2c cx18_i2c_std = {
static const struct cx18_card cx18_card_hvr1600_esmt = {
.type = CX18_CARD_HVR_1600_ESMT,
.name = "Hauppauge HVR-1600",
- .comment = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_CS5345,
@@ -96,7 +97,7 @@ static const struct cx18_card cx18_card_hvr1600_esmt = {
static const struct cx18_card cx18_card_hvr1600_samsung = {
.type = CX18_CARD_HVR_1600_SAMSUNG,
.name = "Hauppauge HVR-1600 (Preproduction)",
- .comment = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_CS5345,
@@ -151,7 +152,7 @@ static const struct cx18_card_pci_info cx18_pci_h900[] = {
static const struct cx18_card cx18_card_h900 = {
.type = CX18_CARD_COMPRO_H900,
.name = "Compro VideoMate H900",
- .comment = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_all = CX18_HW_TUNER,
@@ -248,7 +249,7 @@ static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = {
static const struct cx18_card cx18_card_cnxt_raptor_pal = {
.type = CX18_CARD_CNXT_RAPTOR_PAL,
.name = "Conexant Raptor PAL/SECAM",
- .comment = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_GPIO,
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h
index 32155f6e6fe4..6fa7bcb42dde 100644
--- a/drivers/media/video/cx18/cx18-cards.h
+++ b/drivers/media/video/cx18/cx18-cards.h
@@ -4,6 +4,7 @@
* Derived from ivtv-cards.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -47,8 +48,9 @@
/* V4L2 capability aliases */
#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
- V4L2_CAP_AUDIO | V4L2_CAP_READWRITE)
-/* | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \
+ V4L2_CAP_VBI_CAPTURE)
+/* | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */
struct cx18_card_video_input {
u8 video_type; /* video input type */
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
index f46c7e5ed747..17edf305d649 100644
--- a/drivers/media/video/cx18/cx18-controls.c
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -259,6 +259,7 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
return err;
}
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ struct cx18_api_func_private priv;
struct cx2341x_mpeg_params p = cx->params;
int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing),
c, VIDIOC_S_EXT_CTRLS);
@@ -278,7 +279,9 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
fmt.fmt.pix.height = cx->params.height;
cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
}
- err = cx2341x_update(cx, cx18_api_func, &cx->params, &p);
+ priv.cx = cx;
+ priv.s = &cx->streams[id->type];
+ err = cx2341x_update(&priv, cx18_api_func, &cx->params, &p);
if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt)
err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
cx->params = p;
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 7874d9790a51..f50cf2167adc 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -75,48 +75,76 @@ static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1 };
-static int mmio_ndelay[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1 };
static unsigned cardtype_c = 1;
static unsigned tuner_c = 1;
static unsigned radio_c = 1;
-static unsigned mmio_ndelay_c = 1;
static char pal[] = "--";
static char secam[] = "--";
static char ntsc[] = "-";
/* Buffers */
-static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
+static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS;
static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
+static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
+static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
+static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
+static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
+/* VBI bufsize based on standards supported by card tuner for now */
+static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
+
+static int enc_ts_bufs = -1;
+static int enc_mpg_bufs = -1;
+static int enc_idx_bufs = -1;
+static int enc_yuv_bufs = -1;
+static int enc_vbi_bufs = -1;
+static int enc_pcm_bufs = -1;
+
+
static int cx18_pci_latency = 1;
-int cx18_retry_mmio = 1;
+static int mmio_ndelay;
+static int retry_mmio = 1;
+
int cx18_debug;
module_param_array(tuner, int, &tuner_c, 0644);
module_param_array(radio, bool, &radio_c, 0644);
module_param_array(cardtype, int, &cardtype_c, 0644);
-module_param_array(mmio_ndelay, int, &mmio_ndelay_c, 0644);
module_param_string(pal, pal, sizeof(pal), 0644);
module_param_string(secam, secam, sizeof(secam), 0644);
module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
module_param_named(debug, cx18_debug, int, 0644);
-module_param_named(retry_mmio, cx18_retry_mmio, int, 0644);
+module_param(mmio_ndelay, int, 0644);
+module_param(retry_mmio, int, 0644);
module_param(cx18_pci_latency, int, 0644);
module_param(cx18_first_minor, int, 0644);
-module_param(enc_mpg_buffers, int, 0644);
module_param(enc_ts_buffers, int, 0644);
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_idx_buffers, int, 0644);
module_param(enc_yuv_buffers, int, 0644);
module_param(enc_vbi_buffers, int, 0644);
module_param(enc_pcm_buffers, int, 0644);
+module_param(enc_ts_bufsize, int, 0644);
+module_param(enc_mpg_bufsize, int, 0644);
+module_param(enc_idx_bufsize, int, 0644);
+module_param(enc_yuv_bufsize, int, 0644);
+/* VBI bufsize based on standards supported by card tuner for now */
+module_param(enc_pcm_bufsize, int, 0644);
+
+module_param(enc_ts_bufs, int, 0644);
+module_param(enc_mpg_bufs, int, 0644);
+module_param(enc_idx_bufs, int, 0644);
+module_param(enc_yuv_bufs, int, 0644);
+module_param(enc_vbi_bufs, int, 0644);
+module_param(enc_pcm_bufs, int, 0644);
+
MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
"\t\t\tsee tuner.h for values");
MODULE_PARM_DESC(radio,
@@ -152,28 +180,62 @@ MODULE_PARM_DESC(cx18_pci_latency,
"Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
"\t\t\tDefault: Yes");
MODULE_PARM_DESC(retry_mmio,
- "Check and retry memory mapped IO accesses\n"
- "\t\t\tDefault: 1 [Yes]");
+ "(Deprecated) MMIO writes are now always checked and retried\n"
+ "\t\t\tEffectively: 1 [Yes]");
MODULE_PARM_DESC(mmio_ndelay,
- "Delay (ns) for each CX23418 memory mapped IO access.\n"
- "\t\t\tTry larger values that are close to a multiple of the\n"
- "\t\t\tPCI clock period, 30.3 ns, if your card doesn't work.\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_MMIO_NDELAY));
-MODULE_PARM_DESC(enc_mpg_buffers,
- "Encoder MPG Buffers (in MB)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+ "(Deprecated) MMIO accesses are now never purposely delayed\n"
+ "\t\t\tEffectively: 0 ns");
MODULE_PARM_DESC(enc_ts_buffers,
- "Encoder TS Buffers (in MB)\n"
+ "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
+MODULE_PARM_DESC(enc_ts_bufsize,
+ "Size of an encoder TS buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE));
+MODULE_PARM_DESC(enc_ts_bufs,
+ "Number of encoder TS buffers\n"
+ "\t\t\tDefault is computed from other enc_ts_* parameters");
+MODULE_PARM_DESC(enc_mpg_buffers,
+ "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_mpg_bufsize,
+ "Size of an encoder MPG buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE));
+MODULE_PARM_DESC(enc_mpg_bufs,
+ "Number of encoder MPG buffers\n"
+ "\t\t\tDefault is computed from other enc_mpg_* parameters");
+MODULE_PARM_DESC(enc_idx_buffers,
+ "Encoder IDX buffer memory (MB). (enc_idx_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFFERS));
+MODULE_PARM_DESC(enc_idx_bufsize,
+ "Size of an encoder IDX buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFSIZE));
+MODULE_PARM_DESC(enc_idx_bufs,
+ "Number of encoder IDX buffers\n"
+ "\t\t\tDefault is computed from other enc_idx_* parameters");
MODULE_PARM_DESC(enc_yuv_buffers,
- "Encoder YUV Buffers (in MB)\n"
+ "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_bufsize,
+ "Size of an encoder YUV buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFSIZE));
+MODULE_PARM_DESC(enc_yuv_bufs,
+ "Number of encoder YUV buffers\n"
+ "\t\t\tDefault is computed from other enc_yuv_* parameters");
MODULE_PARM_DESC(enc_vbi_buffers,
- "Encoder VBI Buffers (in MB)\n"
+ "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_bufs,
+ "Number of encoder VBI buffers\n"
+ "\t\t\tDefault is computed from enc_vbi_buffers & tuner std");
MODULE_PARM_DESC(enc_pcm_buffers,
- "Encoder PCM buffers (in MB)\n"
+ "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_bufsize,
+ "Size of an encoder PCM buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE));
+MODULE_PARM_DESC(enc_pcm_bufs,
+ "Number of encoder PCM buffers\n"
+ "\t\t\tDefault is computed from other enc_pcm_* parameters");
MODULE_PARM_DESC(cx18_first_minor, "Set kernel number assigned to first card");
@@ -187,7 +249,7 @@ MODULE_VERSION(CX18_VERSION);
/* Generic utility functions */
int cx18_msleep_timeout(unsigned int msecs, int intr)
{
- int timeout = msecs_to_jiffies(msecs);
+ long int timeout = msecs_to_jiffies(msecs);
int sig;
do {
@@ -366,20 +428,69 @@ static void cx18_process_options(struct cx18 *cx)
{
int i, j;
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */
+
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */
+
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = 0; /* computed later */
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
+
+ /* Except for VBI ensure stream_buffers & stream_buf_size are valid */
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ /* User said to use 0 buffers */
+ if (cx->stream_buffers[i] == 0) {
+ cx->options.megabytes[i] = 0;
+ cx->stream_buf_size[i] = 0;
+ continue;
+ }
+ /* User said to use 0 MB total */
+ if (cx->options.megabytes[i] <= 0) {
+ cx->options.megabytes[i] = 0;
+ cx->stream_buffers[i] = 0;
+ cx->stream_buf_size[i] = 0;
+ continue;
+ }
+ /* VBI is computed later or user said buffer has size 0 */
+ if (cx->stream_buf_size[i] <= 0) {
+ if (i != CX18_ENC_STREAM_TYPE_VBI) {
+ cx->options.megabytes[i] = 0;
+ cx->stream_buffers[i] = 0;
+ cx->stream_buf_size[i] = 0;
+ }
+ continue;
+ }
+ if (cx->stream_buffers[i] < 0) {
+ cx->stream_buffers[i] = cx->options.megabytes[i] * 1024
+ / cx->stream_buf_size[i];
+ } else {
+ /* N.B. This might round down to 0 */
+ cx->options.megabytes[i] =
+ cx->stream_buffers[i] * cx->stream_buf_size[i] / 1024;
+ }
+ cx->stream_buf_size[i] *= 1024; /* convert from kB to bytes */
+ }
+
cx->options.cardtype = cardtype[cx->num];
cx->options.tuner = tuner[cx->num];
cx->options.radio = radio[cx->num];
- if (mmio_ndelay[cx->num] < 0)
- cx->options.mmio_ndelay = CX18_DEFAULT_MMIO_NDELAY;
- else
- cx->options.mmio_ndelay = mmio_ndelay[cx->num];
-
cx->std = cx18_parse_std(cx);
if (cx->options.cardtype == -1) {
CX18_INFO("Ignore card\n");
@@ -440,22 +551,30 @@ done:
*/
static int __devinit cx18_init_struct1(struct cx18 *cx)
{
+ int i;
+
cx->base_addr = pci_resource_start(cx->dev, 0);
mutex_init(&cx->serialize_lock);
mutex_init(&cx->i2c_bus_lock[0]);
mutex_init(&cx->i2c_bus_lock[1]);
mutex_init(&cx->gpio_lock);
+ mutex_init(&cx->epu2apu_mb_lock);
+ mutex_init(&cx->epu2cpu_mb_lock);
spin_lock_init(&cx->lock);
cx->work_queue = create_singlethread_workqueue(cx->name);
if (cx->work_queue == NULL) {
- CX18_ERR("Could not create work queue\n");
- return -1;
+ CX18_ERR("Unable to create work hander thread\n");
+ return -ENOMEM;
}
- INIT_WORK(&cx->work, cx18_work_handler);
+ for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) {
+ cx->epu_work_order[i].cx = cx;
+ cx->epu_work_order[i].str = cx->epu_debug_str;
+ INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler);
+ }
/* start counting open_id at 1 */
cx->open_id = 1;
@@ -472,20 +591,55 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
init_waitqueue_head(&cx->cap_w);
init_waitqueue_head(&cx->mb_apu_waitq);
init_waitqueue_head(&cx->mb_cpu_waitq);
- init_waitqueue_head(&cx->mb_epu_waitq);
- init_waitqueue_head(&cx->mb_hpu_waitq);
init_waitqueue_head(&cx->dma_waitq);
/* VBI */
- cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
- cx->vbi.raw_size = 1456;
- cx->vbi.raw_decoder_line_size = 1456;
- cx->vbi.raw_decoder_sav_odd_field = 0x20;
- cx->vbi.raw_decoder_sav_even_field = 0x60;
- cx->vbi.sliced_decoder_line_size = 272;
- cx->vbi.sliced_decoder_sav_odd_field = 0xB0;
- cx->vbi.sliced_decoder_sav_even_field = 0xF0;
+
+ /*
+ * The VBI line sizes depend on the pixel clock and the horiz rate
+ *
+ * (1/Fh)*(2*Fp) = Samples/line
+ * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples
+ *
+ * Sliced VBI is sent as ancillary data during horizontal blanking
+ * Raw VBI is sent as active video samples during vertcal blanking
+ *
+ * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line
+ * length of 720 pixels @ 4:2:2 sampling. Thus...
+ *
+ * For systems that use a 15.734 kHz horizontal rate, such as
+ * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have:
+ *
+ * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line =
+ * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples
+ *
+ * For systems that use a 15.625 kHz horizontal rate, such as
+ * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have:
+ *
+ * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line =
+ * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples
+ *
+ */
+
+ /* FIXME: init these based on tuner std & modify when std changes */
+ /* CX18-AV-Core number of VBI samples output per horizontal line */
+ cx->vbi.raw_decoder_line_size = 1444; /* 4 byte SAV + 2 * 720 */
+ cx->vbi.sliced_decoder_line_size = 272; /* 60 Hz: 268+4, 50 Hz: 280+4 */
+
+ /* CX18-AV-Core VBI samples/line possibly rounded up */
+ cx->vbi.raw_size = 1444; /* Real max size is 1444 */
+ cx->vbi.sliced_size = 284; /* Real max size is 284 */
+
+ /*
+ * CX18-AV-Core SAV/EAV RP codes in VIP 1.x mode
+ * Task Field VerticalBlank HorizontalBlank 0 0 0 0
+ */
+ cx->vbi.raw_decoder_sav_odd_field = 0x20; /* V */
+ cx->vbi.raw_decoder_sav_even_field = 0x60; /* FV */
+ cx->vbi.sliced_decoder_sav_odd_field = 0xB0; /* T VH - actually EAV */
+ cx->vbi.sliced_decoder_sav_even_field = 0xF0; /* TFVH - actually EAV */
return 0;
}
@@ -518,6 +672,7 @@ static void __devinit cx18_init_struct2(struct cx18 *cx)
cx->av_state.aud_input = CX18_AV_AUDIO8;
cx->av_state.audclk_freq = 48000;
cx->av_state.audmode = V4L2_TUNER_MODE_LANG1;
+ /* FIXME - 8 is NTSC value, investigate */
cx->av_state.vbi_line_offset = 8;
}
@@ -662,12 +817,9 @@ static int __devinit cx18_probe(struct pci_dev *dev,
/* PCI Device Setup */
retval = cx18_setup_pci(cx, dev, pci_id);
- if (retval != 0) {
- if (retval == -EIO)
- goto free_workqueue;
- else if (retval == -ENXIO)
- goto free_mem;
- }
+ if (retval != 0)
+ goto free_workqueue;
+
/* save cx in the pci struct for later use */
pci_set_drvdata(dev, cx);
@@ -726,6 +878,7 @@ static int __devinit cx18_probe(struct pci_dev *dev,
goto free_i2c;
}
cx18_init_memory(cx);
+ cx18_init_scb(cx);
/* Register IRQ */
retval = request_irq(cx->dev->irq, cx18_irq_handler,
@@ -739,8 +892,6 @@ static int __devinit cx18_probe(struct pci_dev *dev,
cx->std = V4L2_STD_NTSC_M;
if (cx->options.tuner == -1) {
- int i;
-
for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
if ((cx->std & cx->card->tuners[i].std) == 0)
continue;
@@ -777,13 +928,23 @@ static int __devinit cx18_probe(struct pci_dev *dev,
}
cx->params.video_gop_size = cx->is_60hz ? 15 : 12;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = 0x08000;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = 0x08000;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = 0x01200;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = 0x20000;
+ /*
+ * FIXME: setting the buffer size based on the tuner standard is
+ * suboptimal, as the CVBS and SVideo inputs could use a different std
+ * and the buffer could end up being too small in that case.
+ */
vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2;
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
+ if (cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] < 0)
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] =
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] * 1024 * 1024
+ / vbi_buf_size;
+ else
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] =
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] * vbi_buf_size
+ / (1024 * 1024);
+
if (cx->options.radio > 0)
cx->v4l2_cap |= V4L2_CAP_RADIO;
@@ -844,7 +1005,6 @@ err:
if (retval == 0)
retval = -ENODEV;
CX18_ERR("Error %d on initialization\n", retval);
- cx18_log_statistics(cx);
i = cx->num;
spin_lock(&cx18_cards_lock);
@@ -923,6 +1083,13 @@ int cx18_init_on_first_open(struct cx18 *cx)
return 0;
}
+static void cx18_cancel_epu_work_orders(struct cx18 *cx)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++)
+ cancel_work_sync(&cx->epu_work_order[i].work);
+}
+
static void cx18_remove(struct pci_dev *pci_dev)
{
struct cx18 *cx = pci_get_drvdata(pci_dev);
@@ -940,7 +1107,8 @@ static void cx18_remove(struct pci_dev *pci_dev)
cx18_halt_firmware(cx);
- flush_workqueue(cx->work_queue);
+ cx18_cancel_epu_work_orders(cx);
+
destroy_workqueue(cx->work_queue);
cx18_streams_cleanup(cx, 1);
@@ -955,7 +1123,6 @@ static void cx18_remove(struct pci_dev *pci_dev)
pci_disable_device(cx->dev);
- cx18_log_statistics(cx);
CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num);
}
@@ -1004,6 +1171,7 @@ static void module_cleanup(void)
continue;
kfree(cx18_cards[i]);
}
+
}
module_init(module_start);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index bbdd5f25041d..0d2edebc39b4 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -4,6 +4,7 @@
* Derived from ivtv-driver.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -64,9 +65,6 @@
# error "This driver requires kernel PCI support."
#endif
-/* Default delay to throttle mmio access to the CX23418 */
-#define CX18_DEFAULT_MMIO_NDELAY 0 /* 0 ns = 0 PCI clock(s) / 33 MHz */
-
#define CX18_MEM_OFFSET 0x00000000
#define CX18_MEM_SIZE 0x04000000
#define CX18_REG_OFFSET 0x02000000
@@ -117,6 +115,17 @@
#define CX18_DEFAULT_ENC_VBI_BUFFERS 1
#define CX18_DEFAULT_ENC_PCM_BUFFERS 1
+/* Maximum firmware DMA buffers per stream */
+#define CX18_MAX_FW_MDLS_PER_STREAM 63
+
+/* DMA buffer, default size in kB allocated */
+#define CX18_DEFAULT_ENC_TS_BUFSIZE 32
+#define CX18_DEFAULT_ENC_MPG_BUFSIZE 32
+#define CX18_DEFAULT_ENC_IDX_BUFSIZE 32
+#define CX18_DEFAULT_ENC_YUV_BUFSIZE 128
+/* Default VBI bufsize based on standards supported by card tuner for now */
+#define CX18_DEFAULT_ENC_PCM_BUFSIZE 4
+
/* i2c stuff */
#define I2C_CLIENTS_MAX 16
@@ -176,7 +185,6 @@
#define CX18_MAX_PGM_INDEX (400)
-extern int cx18_retry_mmio; /* enable check & retry of mmio accesses */
extern int cx18_debug;
@@ -185,7 +193,6 @@ struct cx18_options {
int cardtype; /* force card type on load */
int tuner; /* set tuner on load */
int radio; /* enable/disable radio */
- unsigned long mmio_ndelay; /* delay in ns after every PCI mmio access */
};
/* per-buffer bit flags */
@@ -203,11 +210,8 @@ struct cx18_options {
#define CX18_F_I_EOS 4 /* End of encoder stream */
#define CX18_F_I_RADIO_USER 5 /* radio tuner is selected */
#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */
-#define CX18_F_I_HAVE_WORK 15 /* there is work to be done */
-#define CX18_F_I_WORK_HANDLER_DVB 18 /* work to be done for DVB */
#define CX18_F_I_INITED 21 /* set after first open */
#define CX18_F_I_FAILED 22 /* set if first open failed */
-#define CX18_F_I_WORK_INITED 23 /* worker thread initialized */
/* These are the VBI types as they appear in the embedded VBI private packets. */
#define CX18_SLICED_TYPE_TELETEXT_B (1)
@@ -220,6 +224,7 @@ struct cx18_buffer {
dma_addr_t dma_handle;
u32 id;
unsigned long b_flags;
+ unsigned skipped;
char *buf;
u32 bytesused;
@@ -248,6 +253,27 @@ struct cx18_dvb {
struct cx18; /* forward reference */
struct cx18_scb; /* forward reference */
+
+#define CX18_MAX_MDL_ACKS 2
+#define CX18_MAX_EPU_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7)
+/* CPU_DE_RELEASE_MDL can burst CX18_MAX_FW_MDLS_PER_STREAM orders in a group */
+
+#define CX18_F_EWO_MB_STALE_UPON_RECEIPT 0x1
+#define CX18_F_EWO_MB_STALE_WHILE_PROC 0x2
+#define CX18_F_EWO_MB_STALE \
+ (CX18_F_EWO_MB_STALE_UPON_RECEIPT | CX18_F_EWO_MB_STALE_WHILE_PROC)
+
+struct cx18_epu_work_order {
+ struct work_struct work;
+ atomic_t pending;
+ struct cx18 *cx;
+ unsigned long flags;
+ int rpu;
+ struct cx18_mailbox mb;
+ struct cx18_mdl_ack mdl_ack[CX18_MAX_MDL_ACKS];
+ char *str;
+};
+
#define CX18_INVALID_TASK_HANDLE 0xffffffff
struct cx18_stream {
@@ -261,7 +287,7 @@ struct cx18_stream {
unsigned mdl_offset;
u32 id;
- spinlock_t qlock; /* locks access to the queues */
+ struct mutex qlock; /* locks access to the queues */
unsigned long s_flags; /* status flags, see above */
int dma; /* can be PCI_DMA_TODEVICE,
PCI_DMA_FROMDEVICE or
@@ -275,8 +301,8 @@ struct cx18_stream {
/* Buffer Queues */
struct cx18_queue q_free; /* free buffers */
- struct cx18_queue q_full; /* full buffers */
- struct cx18_queue q_io; /* waiting for I/O */
+ struct cx18_queue q_busy; /* busy buffers - in use by firmware */
+ struct cx18_queue q_full; /* full buffers - data for user apps */
/* DVB / Digital Transport */
struct cx18_dvb dvb;
@@ -353,12 +379,7 @@ struct cx18_i2c_algo_callback_data {
int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */
};
-#define CX18_MAX_MMIO_RETRIES 10
-
-struct cx18_mmio_stats {
- atomic_t retried_write[CX18_MAX_MMIO_RETRIES+1];
- atomic_t retried_read[CX18_MAX_MMIO_RETRIES+1];
-};
+#define CX18_MAX_MMIO_WR_RETRIES 10
/* Struct to hold info about cx18 cards */
struct cx18 {
@@ -378,7 +399,9 @@ struct cx18 {
u32 v4l2_cap; /* V4L2 capabilities of card */
u32 hw_flags; /* Hardware description of the board */
unsigned mdl_offset;
- struct cx18_scb __iomem *scb; /* pointer to SCB */
+ struct cx18_scb __iomem *scb; /* pointer to SCB */
+ struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/
+ struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/
struct cx18_av_state av_state;
@@ -397,6 +420,7 @@ struct cx18 {
struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
struct cx18_options options; /* User options */
+ int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */
int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */
struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */
unsigned long i_flags; /* global cx18 flags */
@@ -428,14 +452,17 @@ struct cx18 {
wait_queue_head_t mb_apu_waitq;
wait_queue_head_t mb_cpu_waitq;
- wait_queue_head_t mb_epu_waitq;
- wait_queue_head_t mb_hpu_waitq;
wait_queue_head_t cap_w;
/* when the current DMA is finished this queue is woken up */
wait_queue_head_t dma_waitq;
+ u32 sw1_irq_mask;
+ u32 sw2_irq_mask;
+ u32 hw2_irq_mask;
+
struct workqueue_struct *work_queue;
- struct work_struct work;
+ struct cx18_epu_work_order epu_work_order[CX18_MAX_EPU_WORK_ORDERS];
+ char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */
/* i2c */
struct i2c_adapter i2c_adap[2];
@@ -450,9 +477,6 @@ struct cx18 {
u32 gpio_val;
struct mutex gpio_lock;
- /* Statistics */
- struct cx18_mmio_stats mmio_stats;
-
/* v4l2 and User settings */
/* codec settings */
@@ -481,4 +505,10 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv);
/* First-open initialization: load firmware, etc. */
int cx18_init_on_first_open(struct cx18 *cx);
+/* Test if the current VBI mode is raw (1) or sliced (0) */
+static inline int cx18_raw_vbi(const struct cx18 *cx)
+{
+ return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
+}
+
#endif /* CX18_DRIVER_H */
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
index 4542e2e5e3d7..bd5e6f3fd4d0 100644
--- a/drivers/media/video/cx18/cx18-dvb.c
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -2,6 +2,7 @@
* cx18 functions for DVB support
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,8 +24,6 @@
#include "cx18-dvb.h"
#include "cx18-io.h"
#include "cx18-streams.h"
-#include "cx18-queue.h"
-#include "cx18-scb.h"
#include "cx18-cards.h"
#include "s5h1409.h"
#include "mxl5005s.h"
@@ -109,20 +108,23 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
if (!demux->dmx.frontend)
return -EINVAL;
- if (stream) {
- mutex_lock(&stream->dvb.feedlock);
- if (stream->dvb.feeding++ == 0) {
- CX18_DEBUG_INFO("Starting Transport DMA\n");
- ret = cx18_start_v4l2_encode_stream(stream);
- if (ret < 0) {
- CX18_DEBUG_INFO(
- "Failed to start Transport DMA\n");
- stream->dvb.feeding--;
- }
- } else
- ret = 0;
- mutex_unlock(&stream->dvb.feedlock);
- }
+ if (!stream)
+ return -EINVAL;
+
+ mutex_lock(&stream->dvb.feedlock);
+ if (stream->dvb.feeding++ == 0) {
+ CX18_DEBUG_INFO("Starting Transport DMA\n");
+ set_bit(CX18_F_S_STREAMING, &stream->s_flags);
+ ret = cx18_start_v4l2_encode_stream(stream);
+ if (ret < 0) {
+ CX18_DEBUG_INFO("Failed to start Transport DMA\n");
+ stream->dvb.feeding--;
+ if (stream->dvb.feeding == 0)
+ clear_bit(CX18_F_S_STREAMING, &stream->s_flags);
+ }
+ } else
+ ret = 0;
+ mutex_unlock(&stream->dvb.feedlock);
return ret;
}
@@ -215,6 +217,10 @@ int cx18_dvb_register(struct cx18_stream *stream)
dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
CX18_INFO("DVB Frontend registered\n");
+ CX18_INFO("Registered DVB adapter%d for %s (%d x %d kB)\n",
+ stream->dvb.dvb_adapter.num, stream->name,
+ stream->buffers, stream->buf_size/1024);
+
mutex_init(&dvb->feedlock);
dvb->enabled = 1;
return ret;
@@ -302,24 +308,3 @@ static int dvb_register(struct cx18_stream *stream)
return ret;
}
-
-void cx18_dvb_work_handler(struct cx18 *cx)
-{
- struct cx18_buffer *buf;
- struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_TS];
-
- while ((buf = cx18_dequeue(s, &s->q_full)) != NULL) {
- if (s->dvb.enabled)
- dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
- buf->bytesused);
-
- cx18_enqueue(s, buf, &s->q_free);
- cx18_buf_sync_for_device(s, buf);
- if (s->handle == CX18_INVALID_TASK_HANDLE) /* FIXME: improve */
- continue;
-
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
- (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
- 1, buf->id, s->buf_size);
- }
-}
diff --git a/drivers/media/video/cx18/cx18-dvb.h b/drivers/media/video/cx18/cx18-dvb.h
index bbdcefc87f28..bf8d8f6f5455 100644
--- a/drivers/media/video/cx18/cx18-dvb.h
+++ b/drivers/media/video/cx18/cx18-dvb.h
@@ -23,4 +23,3 @@
int cx18_dvb_register(struct cx18_stream *stream);
void cx18_dvb_unregister(struct cx18_stream *stream);
-void cx18_dvb_work_handler(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index 5f9089907544..425271a29517 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -4,6 +4,7 @@
* Derived from ivtv-fileops.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -66,12 +67,11 @@ static int cx18_claim_stream(struct cx18_open_id *id, int type)
}
s->id = id->open_id;
- /* CX18_DEC_STREAM_TYPE_MPG needs to claim CX18_DEC_STREAM_TYPE_VBI,
- CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
+ /* CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
(provided VBI insertion is on and sliced VBI is selected), for all
other streams we're done */
if (type == CX18_ENC_STREAM_TYPE_MPG &&
- cx->vbi.insert_mpeg && cx->vbi.sliced_in->service_set) {
+ cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) {
vbi_type = CX18_ENC_STREAM_TYPE_VBI;
} else {
return 0;
@@ -185,8 +185,10 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
!test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) {
/* byteswap and process VBI data */
-/* cx18_process_vbi_data(cx, buf, s_vbi->dma_pts, s_vbi->type); */
- cx18_enqueue(s_vbi, buf, &s_vbi->q_free);
+ cx18_process_vbi_data(cx, buf,
+ s_vbi->dma_pts,
+ s_vbi->type);
+ cx18_stream_put_buf_fw(s_vbi, buf);
}
}
buf = &cx->vbi.sliced_mpeg_buf;
@@ -194,11 +196,6 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
return buf;
}
- /* do we have leftover data? */
- buf = cx18_dequeue(s, &s->q_io);
- if (buf)
- return buf;
-
/* do we have new data? */
buf = cx18_dequeue(s, &s->q_full);
if (buf) {
@@ -262,7 +259,7 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
if (len > ucount)
len = ucount;
if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
- cx->vbi.sliced_in->service_set && buf != &cx->vbi.sliced_mpeg_buf) {
+ !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) {
const char *start = buf->buf + buf->readpos;
const char *p = start + 1;
const u8 *q;
@@ -337,8 +334,7 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
/* Each VBI buffer is one frame, the v4l2 API says that for VBI the
frames should arrive one-by-one, so make sure we never output more
than one VBI frame at a time */
- if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
- cx->vbi.sliced_in->service_set)
+ if (s->type == CX18_ENC_STREAM_TYPE_VBI && !cx18_raw_vbi(cx))
single_frame = 1;
for (;;) {
@@ -365,16 +361,10 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
tot_count - tot_written);
if (buf != &cx->vbi.sliced_mpeg_buf) {
- if (buf->readpos == buf->bytesused) {
- cx18_buf_sync_for_device(s, buf);
- cx18_enqueue(s, buf, &s->q_free);
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5,
- s->handle,
- (void __iomem *)&cx->scb->cpu_mdl[buf->id] -
- cx->enc_mem,
- 1, buf->id, s->buf_size);
- } else
- cx18_enqueue(s, buf, &s->q_io);
+ if (buf->readpos == buf->bytesused)
+ cx18_stream_put_buf_fw(s, buf);
+ else
+ cx18_push(s, buf, &s->q_full);
} else if (buf->readpos == buf->bytesused) {
int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
@@ -518,7 +508,7 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
CX18_DEBUG_HI_FILE("Encoder poll\n");
poll_wait(filp, &s->waitq, wait);
- if (atomic_read(&s->q_full.buffers) || atomic_read(&s->q_io.buffers))
+ if (atomic_read(&s->q_full.buffers))
return POLLIN | POLLRDNORM;
if (eof)
return POLLHUP;
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
index 51534428cd00..1fa95da1575e 100644
--- a/drivers/media/video/cx18/cx18-firmware.c
+++ b/drivers/media/video/cx18/cx18-firmware.c
@@ -2,6 +2,7 @@
* cx18 firmware functions
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,6 +26,7 @@
#include "cx18-irq.h"
#include "cx18-firmware.h"
#include "cx18-cards.h"
+#include "cx18-av-core.h"
#include <linux/firmware.h>
#define CX18_PROC_SOFT_RESET 0xc70010
@@ -121,6 +123,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
if (cx18_raw_readl(cx, dst) != *src) {
CX18_ERR("Mismatch at offset %x\n", i);
release_firmware(fw);
+ cx18_setup_page(cx, 0);
return -EIO;
}
dst++;
@@ -131,10 +134,12 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
size = fw->size;
release_firmware(fw);
+ cx18_setup_page(cx, SCB_OFFSET);
return size;
}
-static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
+static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
+ u32 *entry_addr)
{
const struct firmware *fw = NULL;
int i, j;
@@ -149,9 +154,11 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
if (request_firmware(&fw, fn, &cx->dev->dev)) {
CX18_ERR("unable to open firmware %s\n", fn);
CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
+ cx18_setup_page(cx, 0);
return -ENOMEM;
}
+ *entry_addr = 0;
src = (const u32 *)fw->data;
vers = fw->data + sizeof(seghdr);
sz = fw->size;
@@ -168,10 +175,12 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
}
CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr,
seghdr.addr + seghdr.size - 1);
+ if (*entry_addr == 0)
+ *entry_addr = seghdr.addr;
if (offset + seghdr.size > sz)
break;
for (i = 0; i < seghdr.size; i += 4096) {
- cx18_setup_page(cx, offset + i);
+ cx18_setup_page(cx, seghdr.addr + i);
for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
/* no need for endianness conversion on the ppc */
cx18_raw_writel(cx, src[(offset + j) / 4],
@@ -181,6 +190,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
CX18_ERR("Mismatch at offset %x\n",
offset + j);
release_firmware(fw);
+ cx18_setup_page(cx, 0);
return -EIO;
}
}
@@ -192,16 +202,17 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
fn, apu_version, fw->size);
size = fw->size;
release_firmware(fw);
- /* Clear bit0 for APU to start from 0 */
- cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030);
+ cx18_setup_page(cx, 0);
return size;
}
void cx18_halt_firmware(struct cx18 *cx)
{
CX18_DEBUG_INFO("Preparing for firmware halt.\n");
- cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
- cx18_write_reg(cx, 0x00020002, CX18_ADEC_CONTROL);
+ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
+ 0x0000000F, 0x000F000F);
+ cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL,
+ 0x00000002, 0x00020002);
}
void cx18_init_power(struct cx18 *cx, int lowpwr)
@@ -211,9 +222,48 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN);
/* ADEC out of sleep */
- cx18_write_reg(cx, 0x00020000, CX18_ADEC_CONTROL);
-
- /* The fast clock is at 200/245 MHz */
+ cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL,
+ 0x00000000, 0x00020002);
+
+ /*
+ * The PLL parameters are based on the external crystal frequency that
+ * would ideally be:
+ *
+ * NTSC Color subcarrier freq * 8 =
+ * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+ *
+ * The accidents of history and rationale that explain from where this
+ * combination of magic numbers originate can be found in:
+ *
+ * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
+ * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
+ *
+ * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
+ * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
+ *
+ * As Mike Bradley has rightly pointed out, it's not the exact crystal
+ * frequency that matters, only that all parts of the driver and
+ * firmware are using the same value (close to the ideal value).
+ *
+ * Since I have a strong suspicion that, if the firmware ever assumes a
+ * crystal value at all, it will assume 28.636360 MHz, the crystal
+ * freq used in calculations in this driver will be:
+ *
+ * xtal_freq = 28.636360 MHz
+ *
+ * an error of less than 0.13 ppm which is way, way better than any off
+ * the shelf crystal will have for accuracy anyway.
+ *
+ * Below I aim to run the PLLs' VCOs near 400 MHz to minimze errors.
+ *
+ * Many thanks to Jeff Campbell and Mike Bradley for their extensive
+ * investigation, experimentation, testing, and suggested solutions of
+ * of audio/video sync problems with SVideo and CVBS captures.
+ */
+
+ /* the fast clock is at 200/245 MHz */
+ /* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/
+ /* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/
cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7,
CX18_FAST_CLOCK_PLL_FRAC);
@@ -223,16 +273,36 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
/* set slow clock to 125/120 MHz */
- cx18_write_reg(cx, lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT);
- cx18_write_reg(cx, lowpwr ? 0xEBAF05 : 0x18618A8,
+ /* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */
+ /* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */
+ cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT);
+ cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F,
CX18_SLOW_CLOCK_PLL_FRAC);
- cx18_write_reg(cx, 4, CX18_SLOW_CLOCK_PLL_POST);
+ cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST);
/* mpeg clock pll 54MHz */
+ /* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */
cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT);
- cx18_write_reg(cx, 0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC);
+ cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC);
cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
+ /*
+ * VDCLK Integer = 0x0f, Post Divider = 0x04
+ * AIMCLK Integer = 0x0e, Post Divider = 0x16
+ */
+ cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
+
+ /* VDCLK Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
+ cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
+
+ /* AIMCLK Fraction = 0x05227ad */
+ /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz before post-divide */
+ cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+ cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
+
/* Defaults */
/* APU = SC or SC/2 = 125/62.5 */
/* EPU = SC = 125 */
@@ -248,22 +318,34 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
/* VFC = disabled */
/* USB = disabled */
- cx18_write_reg(cx, lowpwr ? 0xFFFF0020 : 0x00060004,
- CX18_CLOCK_SELECT1);
- cx18_write_reg(cx, lowpwr ? 0xFFFF0004 : 0x00060006,
- CX18_CLOCK_SELECT2);
-
- cx18_write_reg(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1);
- cx18_write_reg(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2);
+ if (lowpwr) {
+ cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1,
+ 0x00000020, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2,
+ 0x00000004, 0xFFFFFFFF);
+ } else {
+ /* This doesn't explicitly set every clock select */
+ cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1,
+ 0x00000004, 0x00060006);
+ cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2,
+ 0x00000006, 0x00060006);
+ }
- cx18_write_reg(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1);
- cx18_write_reg(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2);
+ cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1,
+ 0x00000002, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2,
+ 0x00000104, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1,
+ 0x00009026, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2,
+ 0x00003105, 0xFFFFFFFF);
}
void cx18_init_memory(struct cx18 *cx)
{
cx18_msleep_timeout(10, 0);
- cx18_write_reg(cx, 0x10000, CX18_DDR_SOFT_RESET);
+ cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET,
+ 0x00000000, 0x00010001);
cx18_msleep_timeout(10, 0);
cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
@@ -282,13 +364,15 @@ void cx18_init_memory(struct cx18 *cx)
cx18_msleep_timeout(10, 0);
- cx18_write_reg(cx, 0x20000, CX18_DDR_SOFT_RESET);
+ cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET,
+ 0x00000000, 0x00020002);
cx18_msleep_timeout(10, 0);
/* use power-down mode when idle */
cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG);
- cx18_write_reg(cx, 0x10001, CX18_REG_BUS_TIMEOUT_EN);
+ cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN,
+ 0x00000001, 0x00010001);
cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7);
cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR);
@@ -307,51 +391,76 @@ void cx18_init_memory(struct cx18 *cx)
int cx18_firmware_init(struct cx18 *cx)
{
+ u32 fw_entry_addr;
+ int sz, retries;
+ u32 api_args[MAX_MB_ARGUMENTS];
+
/* Allow chip to control CLKRUN */
cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
- cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
+ /* Stop the firmware */
+ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
+ 0x0000000F, 0x000F000F);
cx18_msleep_timeout(1, 0);
+ /* If the CPU is still running */
+ if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) {
+ CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__);
+ return -EIO;
+ }
+
cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
- /* Only if the processor is not running */
- if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) {
- int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
- cx->enc_mem, cx);
-
- cx18_write_enc(cx, 0xE51FF004, 0);
- cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */
- /* Start APU */
- cx18_write_reg(cx, 0x00010000, CX18_PROC_SOFT_RESET);
- cx18_msleep_timeout(500, 0);
-
- sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
- cx->enc_mem, cx);
-
- if (sz > 0) {
- int retries = 0;
-
- /* start the CPU */
- cx18_write_reg(cx, 0x00080000, CX18_PROC_SOFT_RESET);
- while (retries++ < 50) { /* Loop for max 500mS */
- if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET)
- & 1) == 0)
- break;
- cx18_msleep_timeout(10, 0);
- }
- cx18_msleep_timeout(200, 0);
- if (retries == 51) {
- CX18_ERR("Could not start the CPU\n");
- return -EIO;
- }
- }
- if (sz <= 0)
- return -EIO;
+ sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx);
+ if (sz <= 0)
+ return sz;
+
+ /* The SCB & IPC area *must* be correct before starting the firmwares */
+ cx18_init_scb(cx);
+
+ fw_entry_addr = 0;
+ sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx,
+ &fw_entry_addr);
+ if (sz <= 0)
+ return sz;
+
+ /* Start the CPU. The CPU will take care of the APU for us. */
+ cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET,
+ 0x00000000, 0x00080008);
+
+ /* Wait up to 500 ms for the APU to come out of reset */
+ for (retries = 0;
+ retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1;
+ retries++)
+ cx18_msleep_timeout(10, 0);
+
+ cx18_msleep_timeout(200, 0);
+
+ if (retries == 50 &&
+ (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) {
+ CX18_ERR("Could not start the CPU\n");
+ return -EIO;
}
+
+ /*
+ * The CPU had once before set up to receive an interrupt for it's
+ * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an
+ * interrupt when it sends us an ack, but by the time we process it,
+ * that flag in the SW2 status register has been cleared by the CPU
+ * firmware. We'll prevent that not so useful condition from happening
+ * by clearing the CPU's interrupt enables for Ack IRQ's we want to
+ * process.
+ */
+ cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+ /* Try a benign command to see if the CPU is alive and well */
+ sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0);
+ if (sz < 0)
+ return sz;
+
/* initialize GPIO */
- cx18_write_reg(cx, 0x14001400, 0xC78110);
+ cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c
index 0e560421989e..1a99329f33cb 100644
--- a/drivers/media/video/cx18/cx18-gpio.c
+++ b/drivers/media/video/cx18/cx18-gpio.c
@@ -4,6 +4,7 @@
* Derived from ivtv-gpio.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -47,15 +48,19 @@
static void gpio_write(struct cx18 *cx)
{
- u32 dir = cx->gpio_dir;
- u32 val = cx->gpio_val;
-
- cx18_write_reg(cx, (dir & 0xffff) << 16, CX18_REG_GPIO_DIR1);
- cx18_write_reg(cx, ((dir & 0xffff) << 16) | (val & 0xffff),
- CX18_REG_GPIO_OUT1);
- cx18_write_reg(cx, dir & 0xffff0000, CX18_REG_GPIO_DIR2);
- cx18_write_reg_sync(cx, (dir & 0xffff0000) | ((val & 0xffff0000) >> 16),
- CX18_REG_GPIO_OUT2);
+ u32 dir_lo = cx->gpio_dir & 0xffff;
+ u32 val_lo = cx->gpio_val & 0xffff;
+ u32 dir_hi = cx->gpio_dir >> 16;
+ u32 val_hi = cx->gpio_val >> 16;
+
+ cx18_write_reg_expect(cx, dir_lo << 16,
+ CX18_REG_GPIO_DIR1, ~dir_lo, dir_lo);
+ cx18_write_reg_expect(cx, (dir_lo << 16) | val_lo,
+ CX18_REG_GPIO_OUT1, val_lo, dir_lo);
+ cx18_write_reg_expect(cx, dir_hi << 16,
+ CX18_REG_GPIO_DIR2, ~dir_hi, dir_hi);
+ cx18_write_reg_expect(cx, (dir_hi << 16) | val_hi,
+ CX18_REG_GPIO_OUT2, val_hi, dir_hi);
}
void cx18_reset_i2c_slaves_gpio(struct cx18 *cx)
diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h
index beb7424b9944..39ffccc19d8a 100644
--- a/drivers/media/video/cx18/cx18-gpio.h
+++ b/drivers/media/video/cx18/cx18-gpio.h
@@ -4,6 +4,7 @@
* Derived from ivtv-gpio.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
index aa09e557b195..8941f58bed7f 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -4,6 +4,7 @@
* Derived from ivtv-i2c.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,6 +28,7 @@
#include "cx18-gpio.h"
#include "cx18-av-core.h"
#include "cx18-i2c.h"
+#include "cx18-irq.h"
#define CX18_REG_I2C_1_WR 0xf15000
#define CX18_REG_I2C_1_RD 0xf15008
@@ -160,9 +162,9 @@ static void cx18_setscl(void *data, int state)
u32 r = cx18_read_reg(cx, addr);
if (state)
- cx18_write_reg_sync(cx, r | SETSCL_BIT, addr);
+ cx18_write_reg(cx, r | SETSCL_BIT, addr);
else
- cx18_write_reg_sync(cx, r & ~SETSCL_BIT, addr);
+ cx18_write_reg(cx, r & ~SETSCL_BIT, addr);
}
static void cx18_setsda(void *data, int state)
@@ -173,9 +175,9 @@ static void cx18_setsda(void *data, int state)
u32 r = cx18_read_reg(cx, addr);
if (state)
- cx18_write_reg_sync(cx, r | SETSDL_BIT, addr);
+ cx18_write_reg(cx, r | SETSDL_BIT, addr);
else
- cx18_write_reg_sync(cx, r & ~SETSDL_BIT, addr);
+ cx18_write_reg(cx, r & ~SETSDL_BIT, addr);
}
static int cx18_getscl(void *data)
@@ -396,30 +398,33 @@ int init_cx18_i2c(struct cx18 *cx)
if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) {
/* Reset/Unreset I2C hardware block */
/* Clock select 220MHz */
- cx18_write_reg(cx, 0x10000000, 0xc71004);
+ cx18_write_reg_expect(cx, 0x10000000, 0xc71004,
+ 0x00000000, 0x10001000);
/* Clock Enable */
- cx18_write_reg_sync(cx, 0x10001000, 0xc71024);
+ cx18_write_reg_expect(cx, 0x10001000, 0xc71024,
+ 0x00001000, 0x10001000);
}
/* courtesy of Steven Toth <stoth@hauppauge.com> */
- cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c);
+ cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0);
mdelay(10);
- cx18_write_reg_sync(cx, 0x00c000c0, 0xc7001c);
+ cx18_write_reg_expect(cx, 0x00c000c0, 0xc7001c, 0x000000c0, 0x00c000c0);
mdelay(10);
- cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c);
+ cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0);
mdelay(10);
/* Set to edge-triggered intrs. */
- cx18_write_reg_sync(cx, 0x00c00000, 0xc730c8);
+ cx18_write_reg(cx, 0x00c00000, 0xc730c8);
/* Clear any stale intrs */
- cx18_write_reg_sync(cx, 0x00c00000, 0xc730c4);
+ cx18_write_reg_expect(cx, HW2_I2C1_INT|HW2_I2C2_INT, HW2_INT_CLR_STATUS,
+ ~(HW2_I2C1_INT|HW2_I2C2_INT), HW2_I2C1_INT|HW2_I2C2_INT);
/* Hw I2C1 Clock Freq ~100kHz */
- cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR);
+ cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR);
cx18_setscl(&cx->i2c_algo_cb_data[0], 1);
cx18_setsda(&cx->i2c_algo_cb_data[0], 1);
/* Hw I2C2 Clock Freq ~100kHz */
- cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR);
+ cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR);
cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c
index 220fae8d4ad7..ec5b3d7bcc6b 100644
--- a/drivers/media/video/cx18/cx18-io.c
+++ b/drivers/media/video/cx18/cx18-io.c
@@ -24,179 +24,6 @@
#include "cx18-io.h"
#include "cx18-irq.h"
-void cx18_log_statistics(struct cx18 *cx)
-{
- int i;
-
- if (!(cx18_debug & CX18_DBGFLG_INFO))
- return;
-
- for (i = 0; i <= CX18_MAX_MMIO_RETRIES; i++)
- CX18_DEBUG_INFO("retried_write[%d] = %d\n", i,
- atomic_read(&cx->mmio_stats.retried_write[i]));
- for (i = 0; i <= CX18_MAX_MMIO_RETRIES; i++)
- CX18_DEBUG_INFO("retried_read[%d] = %d\n", i,
- atomic_read(&cx->mmio_stats.retried_read[i]));
- return;
-}
-
-void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr)
-{
- int i;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_raw_writel_noretry(cx, val, addr);
- if (val == cx18_raw_readl_noretry(cx, addr))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-u32 cx18_raw_readl_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u32 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_raw_readl_noretry(cx, addr);
- if (val != 0xffffffff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-u16 cx18_raw_readw_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u16 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_raw_readw_noretry(cx, addr);
- if (val != 0xffff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr)
-{
- int i;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_writel_noretry(cx, val, addr);
- if (val == cx18_readl_noretry(cx, addr))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
- u32 eval, u32 mask)
-{
- int i;
- eval &= mask;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_writel_noretry(cx, val, addr);
- if (eval == (cx18_readl_noretry(cx, addr) & mask))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr)
-{
- int i;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_writew_noretry(cx, val, addr);
- if (val == cx18_readw_noretry(cx, addr))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-void cx18_writeb_retry(struct cx18 *cx, u8 val, void __iomem *addr)
-{
- int i;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_writeb_noretry(cx, val, addr);
- if (val == cx18_readb_noretry(cx, addr))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-u32 cx18_readl_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u32 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_readl_noretry(cx, addr);
- if (val != 0xffffffff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-u16 cx18_readw_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u16 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_readw_noretry(cx, addr);
- if (val != 0xffff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u8 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_readb_noretry(cx, addr);
- if (val != 0xff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-void cx18_memcpy_fromio(struct cx18 *cx, void *to,
- const void __iomem *from, unsigned int len)
-{
- const u8 __iomem *src = from;
- u8 *dst = to;
-
- /* Align reads on the CX23418's addresses */
- if ((len > 0) && ((unsigned long) src & 1)) {
- *dst = cx18_readb(cx, src);
- len--;
- dst++;
- src++;
- }
- if ((len > 1) && ((unsigned long) src & 2)) {
- *((u16 *)dst) = cx18_raw_readw(cx, src);
- len -= 2;
- dst += 2;
- src += 2;
- }
- while (len > 3) {
- *((u32 *)dst) = cx18_raw_readl(cx, src);
- len -= 4;
- dst += 4;
- src += 4;
- }
- if (len > 1) {
- *((u16 *)dst) = cx18_raw_readw(cx, src);
- len -= 2;
- dst += 2;
- src += 2;
- }
- if (len > 0)
- *dst = cx18_readb(cx, src);
-}
-
void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count)
{
u8 __iomem *dst = addr;
@@ -230,32 +57,35 @@ void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count)
void cx18_sw1_irq_enable(struct cx18 *cx, u32 val)
{
- u32 r;
cx18_write_reg_expect(cx, val, SW1_INT_STATUS, ~val, val);
- r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI);
- cx18_write_reg(cx, r | val, SW1_INT_ENABLE_PCI);
+ cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | val;
+ cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI);
}
void cx18_sw1_irq_disable(struct cx18 *cx, u32 val)
{
- u32 r;
- r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI);
- cx18_write_reg(cx, r & ~val, SW1_INT_ENABLE_PCI);
+ cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) & ~val;
+ cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI);
}
void cx18_sw2_irq_enable(struct cx18 *cx, u32 val)
{
- u32 r;
cx18_write_reg_expect(cx, val, SW2_INT_STATUS, ~val, val);
- r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI);
- cx18_write_reg(cx, r | val, SW2_INT_ENABLE_PCI);
+ cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | val;
+ cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI);
}
void cx18_sw2_irq_disable(struct cx18 *cx, u32 val)
{
+ cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) & ~val;
+ cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI);
+}
+
+void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val)
+{
u32 r;
- r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI);
- cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_PCI);
+ r = cx18_read_reg(cx, SW2_INT_ENABLE_CPU);
+ cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_CPU);
}
void cx18_setup_page(struct cx18 *cx, u32 addr)
diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h
index 425244453ea7..2635b3a8cc96 100644
--- a/drivers/media/video/cx18/cx18-io.h
+++ b/drivers/media/video/cx18/cx18-io.h
@@ -25,232 +25,125 @@
#include "cx18-driver.h"
-static inline void cx18_io_delay(struct cx18 *cx)
-{
- if (cx->options.mmio_ndelay)
- ndelay(cx->options.mmio_ndelay);
-}
-
/*
* Readback and retry of MMIO access for reliability:
* The concept was suggested by Steve Toth <stoth@linuxtv.org>.
* The implmentation is the fault of Andy Walls <awalls@radix.net>.
+ *
+ * *write* functions are implied to retry the mmio unless suffixed with _noretry
+ * *read* functions never retry the mmio (it never helps to do so)
*/
-/* Statistics gathering */
-static inline
-void cx18_log_write_retries(struct cx18 *cx, int i, const void __iomem *addr)
-{
- if (i > CX18_MAX_MMIO_RETRIES)
- i = CX18_MAX_MMIO_RETRIES;
- atomic_inc(&cx->mmio_stats.retried_write[i]);
- return;
-}
-
-static inline
-void cx18_log_read_retries(struct cx18 *cx, int i, const void __iomem *addr)
+/* Non byteswapping memory mapped IO */
+static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr)
{
- if (i > CX18_MAX_MMIO_RETRIES)
- i = CX18_MAX_MMIO_RETRIES;
- atomic_inc(&cx->mmio_stats.retried_read[i]);
- return;
+ return __raw_readl(addr);
}
-void cx18_log_statistics(struct cx18 *cx);
-
-/* Non byteswapping memory mapped IO */
static inline
void cx18_raw_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
{
__raw_writel(val, addr);
- cx18_io_delay(cx);
}
-void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr);
-
static inline void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr)
{
- if (cx18_retry_mmio)
- cx18_raw_writel_retry(cx, val, addr);
- else
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
cx18_raw_writel_noretry(cx, val, addr);
+ if (val == cx18_raw_readl(cx, addr))
+ break;
+ }
}
-
-static inline
-u32 cx18_raw_readl_noretry(struct cx18 *cx, const void __iomem *addr)
-{
- u32 ret = __raw_readl(addr);
- cx18_io_delay(cx);
- return ret;
-}
-
-u32 cx18_raw_readl_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr)
+/* Normal memory mapped IO */
+static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr)
{
- if (cx18_retry_mmio)
- return cx18_raw_readl_retry(cx, addr);
-
- return cx18_raw_readl_noretry(cx, addr);
+ return readl(addr);
}
-
static inline
-u16 cx18_raw_readw_noretry(struct cx18 *cx, const void __iomem *addr)
+void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
{
- u16 ret = __raw_readw(addr);
- cx18_io_delay(cx);
- return ret;
+ writel(val, addr);
}
-u16 cx18_raw_readw_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u16 cx18_raw_readw(struct cx18 *cx, const void __iomem *addr)
+static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr)
{
- if (cx18_retry_mmio)
- return cx18_raw_readw_retry(cx, addr);
-
- return cx18_raw_readw_noretry(cx, addr);
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+ cx18_writel_noretry(cx, val, addr);
+ if (val == cx18_readl(cx, addr))
+ break;
+ }
}
-
-/* Normal memory mapped IO */
static inline
-void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
+void cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
+ u32 eval, u32 mask)
{
- writel(val, addr);
- cx18_io_delay(cx);
+ int i;
+ u32 r;
+ eval &= mask;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+ cx18_writel_noretry(cx, val, addr);
+ r = cx18_readl(cx, addr);
+ if (r == 0xffffffff && eval != 0xffffffff)
+ continue;
+ if (eval == (r & mask))
+ break;
+ }
}
-void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr);
-
-static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr)
+static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr)
{
- if (cx18_retry_mmio)
- cx18_writel_retry(cx, val, addr);
- else
- cx18_writel_noretry(cx, val, addr);
+ return readw(addr);
}
-void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
- u32 eval, u32 mask);
-
static inline
void cx18_writew_noretry(struct cx18 *cx, u16 val, void __iomem *addr)
{
writew(val, addr);
- cx18_io_delay(cx);
}
-void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr);
-
static inline void cx18_writew(struct cx18 *cx, u16 val, void __iomem *addr)
{
- if (cx18_retry_mmio)
- cx18_writew_retry(cx, val, addr);
- else
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
cx18_writew_noretry(cx, val, addr);
+ if (val == cx18_readw(cx, addr))
+ break;
+ }
}
+static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr)
+{
+ return readb(addr);
+}
static inline
void cx18_writeb_noretry(struct cx18 *cx, u8 val, void __iomem *addr)
{
writeb(val, addr);
- cx18_io_delay(cx);
}
-void cx18_writeb_retry(struct cx18 *cx, u8 val, void __iomem *addr);
-
static inline void cx18_writeb(struct cx18 *cx, u8 val, void __iomem *addr)
{
- if (cx18_retry_mmio)
- cx18_writeb_retry(cx, val, addr);
- else
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
cx18_writeb_noretry(cx, val, addr);
-}
-
-
-static inline u32 cx18_readl_noretry(struct cx18 *cx, const void __iomem *addr)
-{
- u32 ret = readl(addr);
- cx18_io_delay(cx);
- return ret;
-}
-
-u32 cx18_readl_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr)
-{
- if (cx18_retry_mmio)
- return cx18_readl_retry(cx, addr);
-
- return cx18_readl_noretry(cx, addr);
-}
-
-
-static inline u16 cx18_readw_noretry(struct cx18 *cx, const void __iomem *addr)
-{
- u16 ret = readw(addr);
- cx18_io_delay(cx);
- return ret;
-}
-
-u16 cx18_readw_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr)
-{
- if (cx18_retry_mmio)
- return cx18_readw_retry(cx, addr);
-
- return cx18_readw_noretry(cx, addr);
-}
-
-
-static inline u8 cx18_readb_noretry(struct cx18 *cx, const void __iomem *addr)
-{
- u8 ret = readb(addr);
- cx18_io_delay(cx);
- return ret;
-}
-
-u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr)
-{
- if (cx18_retry_mmio)
- return cx18_readb_retry(cx, addr);
-
- return cx18_readb_noretry(cx, addr);
-}
-
-
-static inline
-u32 cx18_write_sync_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
-{
- cx18_writel_noretry(cx, val, addr);
- return cx18_readl_noretry(cx, addr);
+ if (val == cx18_readb(cx, addr))
+ break;
+ }
}
static inline
-u32 cx18_write_sync_retry(struct cx18 *cx, u32 val, void __iomem *addr)
-{
- cx18_writel_retry(cx, val, addr);
- return cx18_readl_retry(cx, addr);
-}
-
-static inline u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr)
+void cx18_memcpy_fromio(struct cx18 *cx, void *to,
+ const void __iomem *from, unsigned int len)
{
- if (cx18_retry_mmio)
- return cx18_write_sync_retry(cx, val, addr);
-
- return cx18_write_sync_noretry(cx, val, addr);
+ memcpy_fromio(to, from, len);
}
-
-void cx18_memcpy_fromio(struct cx18 *cx, void *to,
- const void __iomem *from, unsigned int len);
void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count);
@@ -260,136 +153,39 @@ static inline void cx18_write_reg_noretry(struct cx18 *cx, u32 val, u32 reg)
cx18_writel_noretry(cx, val, cx->reg_mem + reg);
}
-static inline void cx18_write_reg_retry(struct cx18 *cx, u32 val, u32 reg)
-{
- cx18_writel_retry(cx, val, cx->reg_mem + reg);
-}
-
static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg)
{
- if (cx18_retry_mmio)
- cx18_write_reg_retry(cx, val, reg);
- else
- cx18_write_reg_noretry(cx, val, reg);
-}
-
-static inline void _cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg,
- u32 eval, u32 mask)
-{
- _cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask);
+ cx18_writel(cx, val, cx->reg_mem + reg);
}
static inline void cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg,
u32 eval, u32 mask)
{
- if (cx18_retry_mmio)
- _cx18_write_reg_expect(cx, val, reg, eval, mask);
- else
- cx18_write_reg_noretry(cx, val, reg);
-}
-
-
-static inline u32 cx18_read_reg_noretry(struct cx18 *cx, u32 reg)
-{
- return cx18_readl_noretry(cx, cx->reg_mem + reg);
-}
-
-static inline u32 cx18_read_reg_retry(struct cx18 *cx, u32 reg)
-{
- return cx18_readl_retry(cx, cx->reg_mem + reg);
+ cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask);
}
static inline u32 cx18_read_reg(struct cx18 *cx, u32 reg)
{
- if (cx18_retry_mmio)
- return cx18_read_reg_retry(cx, reg);
-
- return cx18_read_reg_noretry(cx, reg);
-}
-
-
-static inline u32 cx18_write_reg_sync_noretry(struct cx18 *cx, u32 val, u32 reg)
-{
- return cx18_write_sync_noretry(cx, val, cx->reg_mem + reg);
-}
-
-static inline u32 cx18_write_reg_sync_retry(struct cx18 *cx, u32 val, u32 reg)
-{
- return cx18_write_sync_retry(cx, val, cx->reg_mem + reg);
-}
-
-static inline u32 cx18_write_reg_sync(struct cx18 *cx, u32 val, u32 reg)
-{
- if (cx18_retry_mmio)
- return cx18_write_reg_sync_retry(cx, val, reg);
-
- return cx18_write_reg_sync_noretry(cx, val, reg);
+ return cx18_readl(cx, cx->reg_mem + reg);
}
/* Access "encoder memory" region of CX23418 memory mapped I/O */
-static inline void cx18_write_enc_noretry(struct cx18 *cx, u32 val, u32 addr)
-{
- cx18_writel_noretry(cx, val, cx->enc_mem + addr);
-}
-
-static inline void cx18_write_enc_retry(struct cx18 *cx, u32 val, u32 addr)
-{
- cx18_writel_retry(cx, val, cx->enc_mem + addr);
-}
-
static inline void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr)
{
- if (cx18_retry_mmio)
- cx18_write_enc_retry(cx, val, addr);
- else
- cx18_write_enc_noretry(cx, val, addr);
-}
-
-
-static inline u32 cx18_read_enc_noretry(struct cx18 *cx, u32 addr)
-{
- return cx18_readl_noretry(cx, cx->enc_mem + addr);
-}
-
-static inline u32 cx18_read_enc_retry(struct cx18 *cx, u32 addr)
-{
- return cx18_readl_retry(cx, cx->enc_mem + addr);
+ cx18_writel(cx, val, cx->enc_mem + addr);
}
static inline u32 cx18_read_enc(struct cx18 *cx, u32 addr)
{
- if (cx18_retry_mmio)
- return cx18_read_enc_retry(cx, addr);
-
- return cx18_read_enc_noretry(cx, addr);
-}
-
-static inline
-u32 cx18_write_enc_sync_noretry(struct cx18 *cx, u32 val, u32 addr)
-{
- return cx18_write_sync_noretry(cx, val, cx->enc_mem + addr);
-}
-
-static inline
-u32 cx18_write_enc_sync_retry(struct cx18 *cx, u32 val, u32 addr)
-{
- return cx18_write_sync_retry(cx, val, cx->enc_mem + addr);
-}
-
-static inline
-u32 cx18_write_enc_sync(struct cx18 *cx, u32 val, u32 addr)
-{
- if (cx18_retry_mmio)
- return cx18_write_enc_sync_retry(cx, val, addr);
-
- return cx18_write_enc_sync_noretry(cx, val, addr);
+ return cx18_readl(cx, cx->enc_mem + addr);
}
void cx18_sw1_irq_enable(struct cx18 *cx, u32 val);
void cx18_sw1_irq_disable(struct cx18 *cx, u32 val);
void cx18_sw2_irq_enable(struct cx18 *cx, u32 val);
void cx18_sw2_irq_disable(struct cx18 *cx, u32 val);
+void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val);
void cx18_setup_page(struct cx18 *cx, u32 addr);
#endif /* CX18_IO_H */
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index f0ca50f5fdde..e6087486f889 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -4,6 +4,7 @@
* Derived from ivtv-ioctl.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -237,13 +238,12 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
if (ret)
return ret;
- if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
- cx->vbi.sliced_in->service_set &&
- atomic_read(&cx->ana_capturing) > 0)
+ if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
return -EBUSY;
cx->vbi.sliced_in->service_set = 0;
- cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
return cx18_g_fmt_vbi_cap(file, fh, fmt);
}
@@ -745,14 +745,12 @@ static int cx18_log_status(struct file *file, void *fh)
continue;
CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
s->name, s->s_flags,
- (s->buffers - atomic_read(&s->q_free.buffers))
- * 100 / s->buffers,
+ atomic_read(&s->q_full.buffers) * 100 / s->buffers,
(s->buffers * s->buf_size) / 1024, s->buffers);
}
CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
(long long)cx->mpg_data_received,
(long long)cx->vbi_data_inserted);
- cx18_log_statistics(cx);
CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num);
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-ioctl.h b/drivers/media/video/cx18/cx18-ioctl.h
index 2222f679d86d..08fe24e9510e 100644
--- a/drivers/media/video/cx18/cx18-ioctl.h
+++ b/drivers/media/video/cx18/cx18-ioctl.h
@@ -4,6 +4,7 @@
* Derived from ivtv-ioctl.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c
index 5fbfbd0f1493..af2f504eda2b 100644
--- a/drivers/media/video/cx18/cx18-irq.c
+++ b/drivers/media/video/cx18/cx18-irq.c
@@ -2,6 +2,7 @@
* cx18 interrupt handling
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,132 +22,9 @@
#include "cx18-driver.h"
#include "cx18-io.h"
-#include "cx18-firmware.h"
-#include "cx18-fileops.h"
-#include "cx18-queue.h"
#include "cx18-irq.h"
-#include "cx18-ioctl.h"
#include "cx18-mailbox.h"
-#include "cx18-vbi.h"
#include "cx18-scb.h"
-#include "cx18-dvb.h"
-
-void cx18_work_handler(struct work_struct *work)
-{
- struct cx18 *cx = container_of(work, struct cx18, work);
- if (test_and_clear_bit(CX18_F_I_WORK_INITED, &cx->i_flags)) {
- struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
- /* This thread must use the FIFO scheduler as it
- * is realtime sensitive. */
- sched_setscheduler(current, SCHED_FIFO, &param);
- }
- if (test_and_clear_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags))
- cx18_dvb_work_handler(cx);
-}
-
-static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb)
-{
- u32 handle = mb->args[0];
- struct cx18_stream *s = NULL;
- struct cx18_buffer *buf;
- u32 off;
- int i;
- int id;
-
- for (i = 0; i < CX18_MAX_STREAMS; i++) {
- s = &cx->streams[i];
- if ((handle == s->handle) && (s->dvb.enabled))
- break;
- if (s->v4l2dev && handle == s->handle)
- break;
- }
- if (i == CX18_MAX_STREAMS) {
- CX18_WARN("Got DMA done notification for unknown/inactive"
- " handle %d\n", handle);
- mb->error = CXERR_NOT_OPEN;
- mb->cmd = 0;
- cx18_mb_ack(cx, mb);
- return;
- }
-
- off = mb->args[1];
- if (mb->args[2] != 1)
- CX18_WARN("Ack struct = %d for %s\n",
- mb->args[2], s->name);
- id = cx18_read_enc(cx, off);
- buf = cx18_queue_get_buf_irq(s, id, cx18_read_enc(cx, off + 4));
- CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
- if (buf) {
- cx18_buf_sync_for_cpu(s, buf);
- if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
- CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n",
- buf->bytesused);
-
- set_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags);
- set_bit(CX18_F_I_HAVE_WORK, &cx->i_flags);
- } else
- set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
- } else {
- CX18_WARN("Could not find buf %d for stream %s\n",
- cx18_read_enc(cx, off), s->name);
- }
- mb->error = 0;
- mb->cmd = 0;
- cx18_mb_ack(cx, mb);
- wake_up(&cx->dma_waitq);
- if (s->id != -1)
- wake_up(&s->waitq);
-}
-
-static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb)
-{
- char str[256] = { 0 };
- char *p;
-
- if (mb->args[1]) {
- cx18_setup_page(cx, mb->args[1]);
- cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252);
- str[252] = 0;
- }
- cx18_mb_ack(cx, mb);
- CX18_DEBUG_INFO("%x %s\n", mb->args[0], str);
- p = strchr(str, '.');
- if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
- CX18_INFO("FW version: %s\n", p - 1);
-}
-
-static void epu_cmd(struct cx18 *cx, u32 sw1)
-{
- struct cx18_mailbox mb;
-
- if (sw1 & IRQ_CPU_TO_EPU) {
- cx18_memcpy_fromio(cx, &mb, &cx->scb->cpu2epu_mb, sizeof(mb));
- mb.error = 0;
-
- switch (mb.cmd) {
- case CX18_EPU_DMA_DONE:
- epu_dma_done(cx, &mb);
- break;
- case CX18_EPU_DEBUG:
- epu_debug(cx, &mb);
- break;
- default:
- CX18_WARN("Unknown CPU_TO_EPU mailbox command %#08x\n",
- mb.cmd);
- break;
- }
- }
-
- if (sw1 & IRQ_APU_TO_EPU) {
- cx18_memcpy_fromio(cx, &mb, &cx->scb->apu2epu_mb, sizeof(mb));
- CX18_WARN("Unknown APU_TO_EPU mailbox command %#08x\n", mb.cmd);
- }
-
- if (sw1 & IRQ_HPU_TO_EPU) {
- cx18_memcpy_fromio(cx, &mb, &cx->scb->hpu2epu_mb, sizeof(mb));
- CX18_WARN("Unknown HPU_TO_EPU mailbox command %#08x\n", mb.cmd);
- }
-}
static void xpu_ack(struct cx18 *cx, u32 sw2)
{
@@ -154,23 +32,24 @@ static void xpu_ack(struct cx18 *cx, u32 sw2)
wake_up(&cx->mb_cpu_waitq);
if (sw2 & IRQ_APU_TO_EPU_ACK)
wake_up(&cx->mb_apu_waitq);
- if (sw2 & IRQ_HPU_TO_EPU_ACK)
- wake_up(&cx->mb_hpu_waitq);
+}
+
+static void epu_cmd(struct cx18 *cx, u32 sw1)
+{
+ if (sw1 & IRQ_CPU_TO_EPU)
+ cx18_api_epu_cmd_irq(cx, CPU);
+ if (sw1 & IRQ_APU_TO_EPU)
+ cx18_api_epu_cmd_irq(cx, APU);
}
irqreturn_t cx18_irq_handler(int irq, void *dev_id)
{
struct cx18 *cx = (struct cx18 *)dev_id;
- u32 sw1, sw1_mask;
- u32 sw2, sw2_mask;
- u32 hw2, hw2_mask;
+ u32 sw1, sw2, hw2;
- sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI);
- sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & sw1_mask;
- sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI);
- sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask;
- hw2_mask = cx18_read_reg(cx, HW2_INT_MASK5_PCI);
- hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & hw2_mask;
+ sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & cx->sw1_irq_mask;
+ sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & cx->sw2_irq_mask;
+ hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & cx->hw2_irq_mask;
if (sw1)
cx18_write_reg_expect(cx, sw1, SW1_INT_STATUS, ~sw1, sw1);
@@ -180,7 +59,15 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id)
cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2);
if (sw1 || sw2 || hw2)
- CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);
+ CX18_DEBUG_HI_IRQ("received interrupts "
+ "SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);
+
+ /*
+ * SW1 responses have to happen first. The sending XPU times out the
+ * incoming mailboxes on us rather rapidly.
+ */
+ if (sw1)
+ epu_cmd(cx, sw1);
/* To do: interrupt-based I2C handling
if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) {
@@ -190,11 +77,5 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id)
if (sw2)
xpu_ack(cx, sw2);
- if (sw1)
- epu_cmd(cx, sw1);
-
- if (test_and_clear_bit(CX18_F_I_HAVE_WORK, &cx->i_flags))
- queue_work(cx->work_queue, &cx->work);
-
return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE;
}
diff --git a/drivers/media/video/cx18/cx18-irq.h b/drivers/media/video/cx18/cx18-irq.h
index 6173ca3bc9e4..91f0b5278ef9 100644
--- a/drivers/media/video/cx18/cx18-irq.h
+++ b/drivers/media/video/cx18/cx18-irq.h
@@ -2,6 +2,7 @@
* cx18 interrupt handling
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,8 +29,7 @@
#define SW1_INT_ENABLE_PCI 0xc7311c
#define SW2_INT_SET 0xc73140
#define SW2_INT_STATUS 0xc73144
+#define SW2_INT_ENABLE_CPU 0xc73158
#define SW2_INT_ENABLE_PCI 0xc7315c
irqreturn_t cx18_irq_handler(int irq, void *dev_id);
-
-void cx18_work_handler(struct work_struct *work);
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index acff7dfb60df..de5e723fdf44 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -2,6 +2,7 @@
* cx18 mailbox functions
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,15 +27,14 @@
#include "cx18-scb.h"
#include "cx18-irq.h"
#include "cx18-mailbox.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+
+static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" };
#define API_FAST (1 << 2) /* Short timeout */
#define API_SLOW (1 << 3) /* Additional 300ms timeout */
-#define APU 0
-#define CPU 1
-#define EPU 2
-#define HPU 3
-
struct cx18_api_info {
u32 cmd;
u8 flags; /* Flags, see above */
@@ -82,8 +82,9 @@ static const struct cx18_api_info api_info[] = {
API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0),
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0),
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST),
- API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST),
API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW),
+ API_ENTRY(APU, CX18_APU_RESETAI, 0),
+ API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0),
API_ENTRY(0, 0, 0),
};
@@ -97,70 +98,175 @@ static const struct cx18_api_info *find_api_info(u32 cmd)
return NULL;
}
-static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu,
- u32 *state, u32 *irq, u32 *req)
+static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
{
- struct cx18_mailbox __iomem *mb = NULL;
- int wait_count = 0;
- u32 ack;
-
- switch (rpu) {
- case APU:
- mb = &cx->scb->epu2apu_mb;
- *state = cx18_readl(cx, &cx->scb->apu_state);
- *irq = cx18_readl(cx, &cx->scb->epu2apu_irq);
- break;
+ char argstr[MAX_MB_ARGUMENTS*11+1];
+ char *p;
+ int i;
- case CPU:
- mb = &cx->scb->epu2cpu_mb;
- *state = cx18_readl(cx, &cx->scb->cpu_state);
- *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq);
- break;
+ if (!(cx18_debug & CX18_DBGFLG_API))
+ return;
- case HPU:
- mb = &cx->scb->epu2hpu_mb;
- *state = cx18_readl(cx, &cx->scb->hpu_state);
- *irq = cx18_readl(cx, &cx->scb->epu2hpu_irq);
- break;
+ for (i = 0, p = argstr; i < MAX_MB_ARGUMENTS; i++, p += 11) {
+ /* kernel snprintf() appends '\0' always */
+ snprintf(p, 12, " %#010x", mb->args[i]);
}
+ CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s"
+ "\n", name, mb->request, mb->ack, mb->cmd, mb->error, argstr);
+}
- if (mb == NULL)
- return mb;
- do {
- *req = cx18_readl(cx, &mb->request);
- ack = cx18_readl(cx, &mb->ack);
- wait_count++;
- } while (*req != ack && wait_count < 600);
+/*
+ * Functions that run in a work_queue work handling context
+ */
- if (*req == ack) {
- (*req)++;
- if (*req == 0 || *req == 0xffffffff)
- *req = 1;
- return mb;
+static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ u32 handle, mdl_ack_count, id;
+ struct cx18_mailbox *mb;
+ struct cx18_mdl_ack *mdl_ack;
+ struct cx18_stream *s;
+ struct cx18_buffer *buf;
+ int i;
+
+ mb = &order->mb;
+ handle = mb->args[0];
+ s = cx18_handle_to_stream(cx, handle);
+
+ if (s == NULL) {
+ CX18_WARN("Got DMA done notification for unknown/inactive"
+ " handle %d, %s mailbox seq no %d\n", handle,
+ (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ?
+ "stale" : "good", mb->request);
+ return;
}
- return NULL;
+
+ mdl_ack_count = mb->args[2];
+ mdl_ack = order->mdl_ack;
+ for (i = 0; i < mdl_ack_count; i++, mdl_ack++) {
+ id = mdl_ack->id;
+ /*
+ * Simple integrity check for processing a stale (and possibly
+ * inconsistent mailbox): make sure the buffer id is in the
+ * valid range for the stream.
+ *
+ * We go through the trouble of dealing with stale mailboxes
+ * because most of the time, the mailbox data is still valid and
+ * unchanged (and in practice the firmware ping-pongs the
+ * two mdl_ack buffers so mdl_acks are not stale).
+ *
+ * There are occasions when we get a half changed mailbox,
+ * which this check catches for a handle & id mismatch. If the
+ * handle and id do correspond, the worst case is that we
+ * completely lost the old buffer, but pick up the new buffer
+ * early (but the new mdl_ack is guaranteed to be good in this
+ * case as the firmware wouldn't point us to a new mdl_ack until
+ * it's filled in).
+ *
+ * cx18_queue_get buf() will detect the lost buffers
+ * and send them back to q_free for fw rotation eventually.
+ */
+ if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) &&
+ !(id >= s->mdl_offset &&
+ id < (s->mdl_offset + s->buffers))) {
+ CX18_WARN("Fell behind! Ignoring stale mailbox with "
+ " inconsistent data. Lost buffer for mailbox "
+ "seq no %d\n", mb->request);
+ break;
+ }
+ buf = cx18_queue_get_buf(s, id, mdl_ack->data_used);
+
+ CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
+ if (buf == NULL) {
+ CX18_WARN("Could not find buf %d for stream %s\n",
+ id, s->name);
+ /* Put as many buffers as possible back into fw use */
+ cx18_stream_load_fw_queue(s);
+ continue;
+ }
+
+ if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
+ CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n",
+ buf->bytesused);
+ dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
+ buf->bytesused);
+ }
+ /* Put as many buffers as possible back into fw use */
+ cx18_stream_load_fw_queue(s);
+ /* Put back TS buffer, since it was removed from all queues */
+ if (s->type == CX18_ENC_STREAM_TYPE_TS)
+ cx18_stream_put_buf_fw(s, buf);
+ }
+ wake_up(&cx->dma_waitq);
+ if (s->id != -1)
+ wake_up(&s->waitq);
}
-long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb)
+static void epu_debug(struct cx18 *cx, struct cx18_epu_work_order *order)
{
- const struct cx18_api_info *info = find_api_info(mb->cmd);
- struct cx18_mailbox __iomem *ack_mb;
- u32 ack_irq;
- u8 rpu = CPU;
+ char *p;
+ char *str = order->str;
- if (info == NULL && mb->cmd) {
- CX18_WARN("Cannot ack unknown command %x\n", mb->cmd);
- return -EINVAL;
- }
- if (info)
- rpu = info->rpu;
+ CX18_DEBUG_INFO("%x %s\n", order->mb.args[0], str);
+ p = strchr(str, '.');
+ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
+ CX18_INFO("FW version: %s\n", p - 1);
+}
- switch (rpu) {
- case HPU:
- ack_irq = IRQ_EPU_TO_HPU_ACK;
- ack_mb = &cx->scb->hpu2epu_mb;
+static void epu_cmd(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ switch (order->rpu) {
+ case CPU:
+ {
+ switch (order->mb.cmd) {
+ case CX18_EPU_DMA_DONE:
+ epu_dma_done(cx, order);
+ break;
+ case CX18_EPU_DEBUG:
+ epu_debug(cx, order);
+ break;
+ default:
+ CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ }
break;
+ }
+ case APU:
+ CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ default:
+ break;
+ }
+}
+
+static
+void free_epu_work_order(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ atomic_set(&order->pending, 0);
+}
+
+void cx18_epu_work_handler(struct work_struct *work)
+{
+ struct cx18_epu_work_order *order =
+ container_of(work, struct cx18_epu_work_order, work);
+ struct cx18 *cx = order->cx;
+ epu_cmd(cx, order);
+ free_epu_work_order(cx, order);
+}
+
+
+/*
+ * Functions that run in an interrupt handling context
+ */
+
+static void mb_ack_irq(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ struct cx18_mailbox __iomem *ack_mb;
+ u32 ack_irq, req;
+
+ switch (order->rpu) {
case APU:
ack_irq = IRQ_EPU_TO_APU_ACK;
ack_mb = &cx->scb->apu2epu_mb;
@@ -170,26 +276,197 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb)
ack_mb = &cx->scb->cpu2epu_mb;
break;
default:
- CX18_WARN("Unknown RPU for command %x\n", mb->cmd);
- return -EINVAL;
+ CX18_WARN("Unhandled RPU (%d) for command %x ack\n",
+ order->rpu, order->mb.cmd);
+ return;
}
- cx18_setup_page(cx, SCB_OFFSET);
- cx18_write_sync(cx, mb->request, &ack_mb->ack);
+ req = order->mb.request;
+ /* Don't ack if the RPU has gotten impatient and timed us out */
+ if (req != cx18_readl(cx, &ack_mb->request) ||
+ req == cx18_readl(cx, &ack_mb->ack)) {
+ CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our "
+ "incoming %s to EPU mailbox (sequence no. %u) "
+ "while processing\n",
+ rpu_str[order->rpu], rpu_str[order->rpu], req);
+ order->flags |= CX18_F_EWO_MB_STALE_WHILE_PROC;
+ return;
+ }
+ cx18_writel(cx, req, &ack_mb->ack);
cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq);
- return 0;
+ return;
+}
+
+static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ u32 handle, mdl_ack_offset, mdl_ack_count;
+ struct cx18_mailbox *mb;
+
+ mb = &order->mb;
+ handle = mb->args[0];
+ mdl_ack_offset = mb->args[1];
+ mdl_ack_count = mb->args[2];
+
+ if (handle == CX18_INVALID_TASK_HANDLE ||
+ mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) {
+ if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+ mb_ack_irq(cx, order);
+ return -1;
+ }
+
+ cx18_memcpy_fromio(cx, order->mdl_ack, cx->enc_mem + mdl_ack_offset,
+ sizeof(struct cx18_mdl_ack) * mdl_ack_count);
+
+ if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+ mb_ack_irq(cx, order);
+ return 1;
+}
+
+static
+int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ u32 str_offset;
+ char *str = order->str;
+
+ str[0] = '\0';
+ str_offset = order->mb.args[1];
+ if (str_offset) {
+ cx18_setup_page(cx, str_offset);
+ cx18_memcpy_fromio(cx, str, cx->enc_mem + str_offset, 252);
+ str[252] = '\0';
+ cx18_setup_page(cx, SCB_OFFSET);
+ }
+
+ if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+ mb_ack_irq(cx, order);
+
+ return str_offset ? 1 : 0;
}
+static inline
+int epu_cmd_irq(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ int ret = -1;
+
+ switch (order->rpu) {
+ case CPU:
+ {
+ switch (order->mb.cmd) {
+ case CX18_EPU_DMA_DONE:
+ ret = epu_dma_done_irq(cx, order);
+ break;
+ case CX18_EPU_DEBUG:
+ ret = epu_debug_irq(cx, order);
+ break;
+ default:
+ CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ }
+ break;
+ }
+ case APU:
+ CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static inline
+struct cx18_epu_work_order *alloc_epu_work_order_irq(struct cx18 *cx)
+{
+ int i;
+ struct cx18_epu_work_order *order = NULL;
+
+ for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) {
+ /*
+ * We only need "pending" atomic to inspect its contents,
+ * and need not do a check and set because:
+ * 1. Any work handler thread only clears "pending" and only
+ * on one, particular work order at a time, per handler thread.
+ * 2. "pending" is only set here, and we're serialized because
+ * we're called in an IRQ handler context.
+ */
+ if (atomic_read(&cx->epu_work_order[i].pending) == 0) {
+ order = &cx->epu_work_order[i];
+ atomic_set(&order->pending, 1);
+ break;
+ }
+ }
+ return order;
+}
+
+void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu)
+{
+ struct cx18_mailbox __iomem *mb;
+ struct cx18_mailbox *order_mb;
+ struct cx18_epu_work_order *order;
+ int submit;
+
+ switch (rpu) {
+ case CPU:
+ mb = &cx->scb->cpu2epu_mb;
+ break;
+ case APU:
+ mb = &cx->scb->apu2epu_mb;
+ break;
+ default:
+ return;
+ }
+
+ order = alloc_epu_work_order_irq(cx);
+ if (order == NULL) {
+ CX18_WARN("Unable to find blank work order form to schedule "
+ "incoming mailbox command processing\n");
+ return;
+ }
+
+ order->flags = 0;
+ order->rpu = rpu;
+ order_mb = &order->mb;
+
+ /* mb->cmd and mb->args[0] through mb->args[2] */
+ cx18_memcpy_fromio(cx, &order_mb->cmd, &mb->cmd, 4 * sizeof(u32));
+ /* mb->request and mb->ack. N.B. we want to read mb->ack last */
+ cx18_memcpy_fromio(cx, &order_mb->request, &mb->request,
+ 2 * sizeof(u32));
+
+ if (order_mb->request == order_mb->ack) {
+ CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our "
+ "incoming %s to EPU mailbox (sequence no. %u)"
+ "\n",
+ rpu_str[rpu], rpu_str[rpu], order_mb->request);
+ dump_mb(cx, order_mb, "incoming");
+ order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT;
+ }
+
+ /*
+ * Individual EPU command processing is responsible for ack-ing
+ * a non-stale mailbox as soon as possible
+ */
+ submit = epu_cmd_irq(cx, order);
+ if (submit > 0) {
+ queue_work(cx->work_queue, &order->work);
+ }
+}
+
+
+/*
+ * Functions called from a non-interrupt, non work_queue context
+ */
static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
{
const struct cx18_api_info *info = find_api_info(cmd);
- u32 state = 0, irq = 0, req, oldreq, err;
+ u32 state, irq, req, ack, err;
struct cx18_mailbox __iomem *mb;
+ u32 __iomem *xpu_state;
wait_queue_head_t *waitq;
- int timeout = 100;
- int cnt = 0;
- int sig = 0;
+ struct mutex *mb_lock;
+ long int timeout, ret;
int i;
if (info == NULL) {
@@ -201,50 +478,104 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
CX18_DEBUG_HI_API("%s\n", info->name);
else
CX18_DEBUG_API("%s\n", info->name);
- cx18_setup_page(cx, SCB_OFFSET);
- mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req);
- if (mb == NULL) {
- CX18_ERR("mb %s busy\n", info->name);
- return -EBUSY;
+ switch (info->rpu) {
+ case APU:
+ waitq = &cx->mb_apu_waitq;
+ mb_lock = &cx->epu2apu_mb_lock;
+ irq = IRQ_EPU_TO_APU;
+ mb = &cx->scb->epu2apu_mb;
+ xpu_state = &cx->scb->apu_state;
+ break;
+ case CPU:
+ waitq = &cx->mb_cpu_waitq;
+ mb_lock = &cx->epu2cpu_mb_lock;
+ irq = IRQ_EPU_TO_CPU;
+ mb = &cx->scb->epu2cpu_mb;
+ xpu_state = &cx->scb->cpu_state;
+ break;
+ default:
+ CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu);
+ return -EINVAL;
}
- oldreq = req - 1;
+ mutex_lock(mb_lock);
+ /*
+ * Wait for an in-use mailbox to complete
+ *
+ * If the XPU is responding with Ack's, the mailbox shouldn't be in
+ * a busy state, since we serialize access to it on our end.
+ *
+ * If the wait for ack after sending a previous command was interrupted
+ * by a signal, we may get here and find a busy mailbox. After waiting,
+ * mark it "not busy" from our end, if the XPU hasn't ack'ed it still.
+ */
+ state = cx18_readl(cx, xpu_state);
+ req = cx18_readl(cx, &mb->request);
+ timeout = msecs_to_jiffies(10);
+ ret = wait_event_timeout(*waitq,
+ (ack = cx18_readl(cx, &mb->ack)) == req,
+ timeout);
+ if (req != ack) {
+ /* waited long enough, make the mbox "not busy" from our end */
+ cx18_writel(cx, req, &mb->ack);
+ CX18_ERR("mbox was found stuck busy when setting up for %s; "
+ "clearing busy and trying to proceed\n", info->name);
+ } else if (ret != timeout)
+ CX18_DEBUG_API("waited %u msecs for busy mbox to be acked\n",
+ jiffies_to_msecs(timeout-ret));
+
+ /* Build the outgoing mailbox */
+ req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1;
+
cx18_writel(cx, cmd, &mb->cmd);
for (i = 0; i < args; i++)
cx18_writel(cx, data[i], &mb->args[i]);
cx18_writel(cx, 0, &mb->error);
cx18_writel(cx, req, &mb->request);
+ cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */
- switch (info->rpu) {
- case APU: waitq = &cx->mb_apu_waitq; break;
- case CPU: waitq = &cx->mb_cpu_waitq; break;
- case EPU: waitq = &cx->mb_epu_waitq; break;
- case HPU: waitq = &cx->mb_hpu_waitq; break;
- default: return -EINVAL;
- }
- if (info->flags & API_FAST)
- timeout /= 2;
+ /*
+ * Notify the XPU and wait for it to send an Ack back
+ */
+ timeout = msecs_to_jiffies((info->flags & API_FAST) ? 10 : 20);
+
+ CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n",
+ irq, info->name);
cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq);
- while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request)
- && cnt < 660) {
- if (cnt > 200 && !in_atomic())
- sig = cx18_msleep_timeout(10, 1);
- cnt++;
- }
- if (sig)
- return -EINTR;
- if (cnt == 660) {
- cx18_writel(cx, oldreq, &mb->request);
- CX18_ERR("mb %s failed\n", info->name);
+ ret = wait_event_timeout(
+ *waitq,
+ cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request),
+ timeout);
+
+ if (ret == 0) {
+ /* Timed out */
+ mutex_unlock(mb_lock);
+ CX18_DEBUG_WARN("sending %s timed out waiting %d msecs for RPU "
+ "acknowledgement\n",
+ info->name, jiffies_to_msecs(timeout));
return -EINVAL;
}
+
+ if (ret != timeout)
+ CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n",
+ jiffies_to_msecs(timeout-ret), info->name);
+
+ /* Collect data returned by the XPU */
for (i = 0; i < MAX_MB_ARGUMENTS; i++)
data[i] = cx18_readl(cx, &mb->args[i]);
err = cx18_readl(cx, &mb->error);
- if (!in_atomic() && (info->flags & API_SLOW))
+ mutex_unlock(mb_lock);
+
+ /*
+ * Wait for XPU to perform extra actions for the caller in some cases.
+ * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers
+ * back in a burst shortly thereafter
+ */
+ if (info->flags & API_SLOW)
cx18_msleep_timeout(300, 0);
+
if (err)
CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
info->name);
@@ -253,12 +584,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])
{
- int res = cx18_api_call(cx, cmd, args, data);
-
- /* Allow a single retry, probably already too late though.
- If there is no free mailbox then that is usually an indication
- of a more serious problem. */
- return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res;
+ return cx18_api_call(cx, cmd, args, data);
}
static int cx18_set_filter_param(struct cx18_stream *s)
@@ -281,8 +607,9 @@ static int cx18_set_filter_param(struct cx18_stream *s)
int cx18_api_func(void *priv, u32 cmd, int in, int out,
u32 data[CX2341X_MBOX_MAX_DATA])
{
- struct cx18 *cx = priv;
- struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+ struct cx18_api_func_private *api_priv = priv;
+ struct cx18 *cx = api_priv->cx;
+ struct cx18_stream *s = api_priv->s;
switch (cmd) {
case CX2341X_ENC_SET_OUTPUT_PORT:
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h
index d995641536b3..ce2b6686aa00 100644
--- a/drivers/media/video/cx18/cx18-mailbox.h
+++ b/drivers/media/video/cx18/cx18-mailbox.h
@@ -2,6 +2,7 @@
* cx18 mailbox functions
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,8 +31,24 @@
#define MB_RESERVED_HANDLE_0 0
#define MB_RESERVED_HANDLE_1 0xFFFFFFFF
+#define APU 0
+#define CPU 1
+#define EPU 2
+#define HPU 3
+
struct cx18;
+/*
+ * This structure is used by CPU to provide completed buffers information
+ * Its structure is dictrated by the layout of the SCB, required by the
+ * firmware, but its defintion needs to be here, instead of in cx18-scb.h,
+ * for mailbox work order scheduling
+ */
+struct cx18_mdl_ack {
+ u32 id; /* ID of a completed MDL */
+ u32 data_used; /* Total data filled in the MDL for buffer 'id' */
+};
+
/* The cx18_mailbox struct is the mailbox structure which is used for passing
messages between processors */
struct cx18_mailbox {
@@ -62,12 +79,22 @@ struct cx18_mailbox {
u32 error;
};
+struct cx18_stream;
+
+struct cx18_api_func_private {
+ struct cx18 *cx;
+ struct cx18_stream *s;
+};
+
int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]);
int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd,
int args, ...);
int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...);
int cx18_api_func(void *priv, u32 cmd, int in, int out,
u32 data[CX2341X_MBOX_MAX_DATA]);
-long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb);
+
+void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu);
+
+void cx18_epu_work_handler(struct work_struct *work);
#endif
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index 174682c2582f..8d9441e88c4e 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -4,6 +4,7 @@
* Derived from ivtv-queue.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -41,91 +42,126 @@ void cx18_queue_init(struct cx18_queue *q)
q->bytesused = 0;
}
-void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
- struct cx18_queue *q)
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q, int to_front)
{
- unsigned long flags = 0;
-
- /* clear the buffer if it is going to be enqueued to the free queue */
- if (q == &s->q_free) {
+ /* clear the buffer if it is not to be enqueued to the full queue */
+ if (q != &s->q_full) {
buf->bytesused = 0;
buf->readpos = 0;
buf->b_flags = 0;
+ buf->skipped = 0;
}
- spin_lock_irqsave(&s->qlock, flags);
- list_add_tail(&buf->list, &q->list);
- atomic_inc(&q->buffers);
+
+ mutex_lock(&s->qlock);
+
+ /* q_busy is restricted to a max buffer count imposed by firmware */
+ if (q == &s->q_busy &&
+ atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ q = &s->q_free;
+
+ if (to_front)
+ list_add(&buf->list, &q->list); /* LIFO */
+ else
+ list_add_tail(&buf->list, &q->list); /* FIFO */
q->bytesused += buf->bytesused - buf->readpos;
- spin_unlock_irqrestore(&s->qlock, flags);
+ atomic_inc(&q->buffers);
+
+ mutex_unlock(&s->qlock);
+ return q;
}
struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
{
struct cx18_buffer *buf = NULL;
- unsigned long flags = 0;
- spin_lock_irqsave(&s->qlock, flags);
+ mutex_lock(&s->qlock);
if (!list_empty(&q->list)) {
- buf = list_entry(q->list.next, struct cx18_buffer, list);
- list_del_init(q->list.next);
- atomic_dec(&q->buffers);
+ buf = list_first_entry(&q->list, struct cx18_buffer, list);
+ list_del_init(&buf->list);
q->bytesused -= buf->bytesused - buf->readpos;
+ buf->skipped = 0;
+ atomic_dec(&q->buffers);
}
- spin_unlock_irqrestore(&s->qlock, flags);
+ mutex_unlock(&s->qlock);
return buf;
}
-struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id,
+struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
u32 bytesused)
{
struct cx18 *cx = s->cx;
- struct list_head *p;
-
- spin_lock(&s->qlock);
- list_for_each(p, &s->q_free.list) {
- struct cx18_buffer *buf =
- list_entry(p, struct cx18_buffer, list);
-
- if (buf->id != id)
+ struct cx18_buffer *buf;
+ struct cx18_buffer *tmp;
+ struct cx18_buffer *ret = NULL;
+
+ mutex_lock(&s->qlock);
+ list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) {
+ if (buf->id != id) {
+ buf->skipped++;
+ if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) {
+ /* buffer must have fallen out of rotation */
+ CX18_WARN("Skipped %s, buffer %d, %d "
+ "times - it must have dropped out of "
+ "rotation\n", s->name, buf->id,
+ buf->skipped);
+ /* move it to q_free */
+ list_move_tail(&buf->list, &s->q_free.list);
+ buf->bytesused = buf->readpos = buf->b_flags =
+ buf->skipped = 0;
+ atomic_dec(&s->q_busy.buffers);
+ atomic_inc(&s->q_free.buffers);
+ }
continue;
+ }
buf->bytesused = bytesused;
- atomic_dec(&s->q_free.buffers);
- atomic_inc(&s->q_full.buffers);
- s->q_full.bytesused += buf->bytesused;
- list_move_tail(&buf->list, &s->q_full.list);
+ /* Sync the buffer before we release the qlock */
+ cx18_buf_sync_for_cpu(s, buf);
+ if (s->type == CX18_ENC_STREAM_TYPE_TS) {
+ /*
+ * TS doesn't use q_full. As we pull the buffer off of
+ * the queue here, the caller will have to put it back.
+ */
+ list_del_init(&buf->list);
+ } else {
+ /* Move buffer from q_busy to q_full */
+ list_move_tail(&buf->list, &s->q_full.list);
+ set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
+ s->q_full.bytesused += buf->bytesused;
+ atomic_inc(&s->q_full.buffers);
+ }
+ atomic_dec(&s->q_busy.buffers);
- spin_unlock(&s->qlock);
- return buf;
+ ret = buf;
+ break;
}
- spin_unlock(&s->qlock);
- CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
- return NULL;
+ mutex_unlock(&s->qlock);
+ return ret;
}
/* Move all buffers of a queue to q_free, while flushing the buffers */
static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q)
{
- unsigned long flags;
struct cx18_buffer *buf;
if (q == &s->q_free)
return;
- spin_lock_irqsave(&s->qlock, flags);
+ mutex_lock(&s->qlock);
while (!list_empty(&q->list)) {
- buf = list_entry(q->list.next, struct cx18_buffer, list);
- list_move_tail(q->list.next, &s->q_free.list);
- buf->bytesused = buf->readpos = buf->b_flags = 0;
+ buf = list_first_entry(&q->list, struct cx18_buffer, list);
+ list_move_tail(&buf->list, &s->q_free.list);
+ buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0;
atomic_inc(&s->q_free.buffers);
}
cx18_queue_init(q);
- spin_unlock_irqrestore(&s->qlock, flags);
+ mutex_unlock(&s->qlock);
}
void cx18_flush_queues(struct cx18_stream *s)
{
- cx18_queue_flush(s, &s->q_io);
+ cx18_queue_flush(s, &s->q_busy);
cx18_queue_flush(s, &s->q_full);
}
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h
index 7f93bb13c09f..456cec3bc28f 100644
--- a/drivers/media/video/cx18/cx18-queue.h
+++ b/drivers/media/video/cx18/cx18-queue.h
@@ -4,6 +4,7 @@
* Derived from ivtv-queue.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -42,11 +43,26 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
void cx18_buf_swap(struct cx18_buffer *buf);
/* cx18_queue utility functions */
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q, int to_front);
+
+static inline
+struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q)
+{
+ return _cx18_enqueue(s, buf, q, 0); /* FIFO */
+}
+
+static inline
+struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q)
+{
+ return _cx18_enqueue(s, buf, q, 1); /* LIFO */
+}
+
void cx18_queue_init(struct cx18_queue *q);
-void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
- struct cx18_queue *q);
struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
-struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id,
+struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
u32 bytesused);
void cx18_flush_queues(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-scb.c b/drivers/media/video/cx18/cx18-scb.c
index f56d3772aa67..34b4d03c55cd 100644
--- a/drivers/media/video/cx18/cx18-scb.c
+++ b/drivers/media/video/cx18/cx18-scb.c
@@ -2,6 +2,7 @@
* cx18 System Control Block initialization
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -117,6 +118,5 @@ void cx18_init_scb(struct cx18 *cx)
cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
&cx->scb->ipc_offset);
- cx18_writel(cx, 1, &cx->scb->hpu_state);
cx18_writel(cx, 1, &cx->scb->epu_state);
}
diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h
index 594713bbed68..1dc1c431f5a1 100644
--- a/drivers/media/video/cx18/cx18-scb.h
+++ b/drivers/media/video/cx18/cx18-scb.h
@@ -2,6 +2,7 @@
* cx18 System Control Block initialization
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -85,12 +86,6 @@ struct cx18_mdl {
u32 length; /* Length of the buffer segment */
};
-/* This structure is used by CPU to provide completed buffers information */
-struct cx18_mdl_ack {
- u32 id; /* ID of a completed MDL */
- u32 data_used; /* Total data filled in the MDL for buffer 'id' */
-};
-
struct cx18_scb {
/* These fields form the System Control Block which is used at boot time
for localizing the IPC data as well as the code positions for all
@@ -276,7 +271,7 @@ struct cx18_scb {
struct cx18_mailbox hpu2epu_mb;
struct cx18_mailbox ppu2epu_mb;
- struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][2];
+ struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS];
struct cx18_mdl cpu_mdl[1];
};
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index e5ff7705b7a1..63c336c95ff5 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -4,6 +4,7 @@
* Derived from ivtv-streams.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -110,7 +111,6 @@ static void cx18_stream_init(struct cx18 *cx, int type)
{
struct cx18_stream *s = &cx->streams[type];
struct video_device *dev = s->v4l2dev;
- u32 max_size = cx->options.megabytes[type] * 1024 * 1024;
/* we need to keep v4l2dev, so restore it afterwards */
memset(s, 0, sizeof(*s));
@@ -123,21 +123,15 @@ static void cx18_stream_init(struct cx18 *cx, int type)
s->handle = CX18_INVALID_TASK_HANDLE;
s->dma = cx18_stream_info[type].dma;
+ s->buffers = cx->stream_buffers[type];
s->buf_size = cx->stream_buf_size[type];
- if (s->buf_size)
- s->buffers = max_size / s->buf_size;
- if (s->buffers > 63) {
- /* Each stream has a maximum of 63 buffers,
- ensure we do not exceed that. */
- s->buffers = 63;
- s->buf_size = (max_size / s->buffers) & ~0xfff;
- }
- spin_lock_init(&s->qlock);
+
+ mutex_init(&s->qlock);
init_waitqueue_head(&s->waitq);
s->id = -1;
cx18_queue_init(&s->q_free);
+ cx18_queue_init(&s->q_busy);
cx18_queue_init(&s->q_full);
- cx18_queue_init(&s->q_io);
}
static int cx18_prep_dev(struct cx18 *cx, int type)
@@ -167,7 +161,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
/* User explicitly selected 0 buffers for these streams, so don't
create them. */
if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
- cx->options.megabytes[type] == 0) {
+ cx->stream_buffers[type] == 0) {
CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
return 0;
}
@@ -267,8 +261,9 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
switch (vfl_type) {
case VFL_TYPE_GRABBER:
- CX18_INFO("Registered device video%d for %s (%d MB)\n",
- num, s->name, cx->options.megabytes[type]);
+ CX18_INFO("Registered device video%d for %s (%d x %d kB)\n",
+ num, s->name, cx->stream_buffers[type],
+ cx->stream_buf_size[type]/1024);
break;
case VFL_TYPE_RADIO:
@@ -277,10 +272,11 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
break;
case VFL_TYPE_VBI:
- if (cx->options.megabytes[type])
- CX18_INFO("Registered device vbi%d for %s (%d MB)\n",
- num,
- s->name, cx->options.megabytes[type]);
+ if (cx->stream_buffers[type])
+ CX18_INFO("Registered device vbi%d for %s "
+ "(%d x %d bytes)\n",
+ num, s->name, cx->stream_buffers[type],
+ cx->stream_buf_size[type]);
else
CX18_INFO("Registered device vbi%d for %s\n",
num, s->name);
@@ -344,7 +340,7 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister)
static void cx18_vbi_setup(struct cx18_stream *s)
{
struct cx18 *cx = s->cx;
- int raw = cx->vbi.sliced_in->service_set == 0;
+ int raw = cx18_raw_vbi(cx);
u32 data[CX2341X_MBOX_MAX_DATA];
int lines;
@@ -362,8 +358,7 @@ static void cx18_vbi_setup(struct cx18_stream *s)
cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
/* determine number of lines and total number of VBI bytes.
- A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
- The '- 1' byte is probably an unused U or V byte. Or something...
+ A raw line takes 1444 bytes: 4 byte SAV code + 2 * 720
A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
header, 42 data bytes + checksum (to be confirmed) */
if (raw) {
@@ -381,14 +376,15 @@ static void cx18_vbi_setup(struct cx18_stream *s)
/* Lines per field */
data[1] = (lines / 2) | ((lines / 2) << 16);
/* bytes per line */
- data[2] = (raw ? cx->vbi.raw_size : cx->vbi.sliced_size);
+ data[2] = (raw ? cx->vbi.raw_decoder_line_size
+ : cx->vbi.sliced_decoder_line_size);
/* Every X number of frames a VBI interrupt arrives
(frames as in 25 or 30 fps) */
data[3] = 1;
/* Setup VBI for the cx25840 digitizer */
if (raw) {
data[4] = 0x20602060;
- data[5] = 0x30703070;
+ data[5] = 0x307090d0;
} else {
data[4] = 0xB0F0B0F0;
data[5] = 0xA0E0A0E0;
@@ -401,11 +397,52 @@ static void cx18_vbi_setup(struct cx18_stream *s)
cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
}
+struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s,
+ struct cx18_buffer *buf)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_queue *q;
+
+ /* Don't give it to the firmware, if we're not running a capture */
+ if (s->handle == CX18_INVALID_TASK_HANDLE ||
+ !test_bit(CX18_F_S_STREAMING, &s->s_flags))
+ return cx18_enqueue(s, buf, &s->q_free);
+
+ q = cx18_enqueue(s, buf, &s->q_busy);
+ if (q != &s->q_busy)
+ return q; /* The firmware has the max buffers it can handle */
+
+ cx18_buf_sync_for_device(s, buf);
+ cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
+ (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
+ 1, buf->id, s->buf_size);
+ return q;
+}
+
+void cx18_stream_load_fw_queue(struct cx18_stream *s)
+{
+ struct cx18_queue *q;
+ struct cx18_buffer *buf;
+
+ if (atomic_read(&s->q_free.buffers) == 0 ||
+ atomic_read(&s->q_busy.buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ return;
+
+ /* Move from q_free to q_busy notifying the firmware, until the limit */
+ do {
+ buf = cx18_dequeue(s, &s->q_free);
+ if (buf == NULL)
+ break;
+ q = cx18_stream_put_buf_fw(s, buf);
+ } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM
+ && q == &s->q_busy);
+}
+
int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
{
u32 data[MAX_MB_ARGUMENTS];
struct cx18 *cx = s->cx;
- struct list_head *p;
+ struct cx18_buffer *buf;
int ts = 0;
int captype = 0;
@@ -434,8 +471,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
captype = CAPTURE_CHANNEL_TYPE_PCM;
break;
case CX18_ENC_STREAM_TYPE_VBI:
- captype = cx->vbi.sliced_in->service_set ?
- CAPTURE_CHANNEL_TYPE_SLICED_VBI : CAPTURE_CHANNEL_TYPE_VBI;
+ captype = cx18_raw_vbi(cx) ?
+ CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI;
cx->vbi.frame = 0;
cx->vbi.inserted_frame = 0;
memset(cx->vbi.sliced_mpeg_size,
@@ -457,6 +494,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
if (atomic_read(&cx->ana_capturing) == 0 && !ts) {
+ struct cx18_api_func_private priv;
+
/* Stuff from Windows, we don't know what it is */
cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
@@ -476,7 +515,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0);
/* Setup API for Stream */
- cx2341x_update(cx, cx18_api_func, NULL, &cx->params);
+ priv.cx = cx;
+ priv.s = s;
+ cx2341x_update(&priv, cx18_api_func, NULL, &cx->params);
}
if (atomic_read(&cx->tot_capturing) == 0) {
@@ -488,16 +529,17 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
- list_for_each(p, &s->q_free.list) {
- struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list);
-
+ /* Init all the cpu_mdls for this stream */
+ cx18_flush_queues(s);
+ mutex_lock(&s->qlock);
+ list_for_each_entry(buf, &s->q_free.list, list) {
cx18_writel(cx, buf->dma_handle,
&cx->scb->cpu_mdl[buf->id].paddr);
cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
- (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
- 1, buf->id, s->buf_size);
}
+ mutex_unlock(&s->qlock);
+ cx18_stream_load_fw_queue(s);
+
/* begin_capture */
if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
CX18_DEBUG_WARN("Error starting capture!\n");
@@ -506,9 +548,15 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1);
else
cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+ /* FIXME - CX18_F_S_STREAMOFF as well? */
cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
- /* FIXME - clean-up DSP0_INT mask, i_flags, s_flags, etc. */
+ s->handle = CX18_INVALID_TASK_HANDLE;
+ if (atomic_read(&cx->tot_capturing) == 0) {
+ set_bit(CX18_F_I_EOS, &cx->i_flags);
+ cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
+ }
return -EINVAL;
}
@@ -560,9 +608,6 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
}
- /* Tell the CX23418 it can't use our buffers anymore */
- cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
-
if (s->type != CX18_ENC_STREAM_TYPE_TS)
atomic_dec(&cx->ana_capturing);
atomic_dec(&cx->tot_capturing);
@@ -570,6 +615,9 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
/* Clear capture and no-read bits */
clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+ /* Tell the CX23418 it can't use our buffers anymore */
+ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
+
cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
s->handle = CX18_INVALID_TASK_HANDLE;
@@ -595,3 +643,21 @@ u32 cx18_find_handle(struct cx18 *cx)
}
return CX18_INVALID_TASK_HANDLE;
}
+
+struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
+{
+ int i;
+ struct cx18_stream *s;
+
+ if (handle == CX18_INVALID_TASK_HANDLE)
+ return NULL;
+
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ s = &cx->streams[i];
+ if (s->handle != handle)
+ continue;
+ if (s->v4l2dev || s->dvb.enabled)
+ return s;
+ }
+ return NULL;
+}
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h
index f327e947b24f..420e0a172945 100644
--- a/drivers/media/video/cx18/cx18-streams.h
+++ b/drivers/media/video/cx18/cx18-streams.h
@@ -4,6 +4,7 @@
* Derived from ivtv-streams.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,11 +23,15 @@
*/
u32 cx18_find_handle(struct cx18 *cx);
+struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle);
int cx18_streams_setup(struct cx18 *cx);
int cx18_streams_register(struct cx18 *cx);
void cx18_streams_cleanup(struct cx18 *cx, int unregister);
/* Capture related */
+void cx18_stream_load_fw_queue(struct cx18_stream *s);
+struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s,
+ struct cx18_buffer *buf);
int cx18_start_v4l2_encode_stream(struct cx18_stream *s);
int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end);
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c
index 22e76ee3f447..fb595bd548e8 100644
--- a/drivers/media/video/cx18/cx18-vbi.c
+++ b/drivers/media/video/cx18/cx18-vbi.c
@@ -160,11 +160,14 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
return;
/* Raw VBI data */
- if (cx->vbi.sliced_in->service_set == 0) {
+ if (cx18_raw_vbi(cx)) {
u8 type;
cx18_buf_swap(buf);
+ /* Skip 12 bytes of header that gets stuffed in */
+ size -= 12;
+ memcpy(p, &buf->buf[12], size);
type = p[3];
size = buf->bytesused = compress_raw_buf(cx, p, size);
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
index 9f6be2d457fb..84c0ff13b607 100644
--- a/drivers/media/video/cx18/cx18-version.h
+++ b/drivers/media/video/cx18/cx18-version.h
@@ -25,7 +25,7 @@
#define CX18_DRIVER_NAME "cx18"
#define CX18_DRIVER_VERSION_MAJOR 1
#define CX18_DRIVER_VERSION_MINOR 0
-#define CX18_DRIVER_VERSION_PATCHLEVEL 1
+#define CX18_DRIVER_VERSION_PATCHLEVEL 4
#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
#define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
index 668f968d7761..601f3a2ab742 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/video/cx18/cx23418.h
@@ -44,6 +44,7 @@
/* All commands for CPU have the following mask set */
#define CPU_CMD_MASK 0x20000000
+#define CPU_CMD_MASK_DEBUG (CPU_CMD_MASK | 0x00000000)
#define CPU_CMD_MASK_ACK (CPU_CMD_MASK | 0x80000000)
#define CPU_CMD_MASK_CAPTURE (CPU_CMD_MASK | 0x00020000)
#define CPU_CMD_MASK_TS (CPU_CMD_MASK | 0x00040000)
@@ -71,6 +72,11 @@
0/zero/NULL means "I have nothing to say" */
#define CX18_EPU_DEBUG (EPU_CMD_MASK_DEBUG | 0x0003)
+/* Reads memory/registers (32-bit)
+ IN[0] - Address
+ OUT[1] - Value */
+#define CX18_CPU_DEBUG_PEEK32 (CPU_CMD_MASK_DEBUG | 0x0003)
+
/* Description: This command starts streaming with the set channel type
IN[0] - Task handle. Handle of the task to start
ReturnCode - One of the ERR_CAPTURE_... */
diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
index 00831f3ef8f5..798d24024353 100644
--- a/drivers/media/video/cx23885/cx23885-417.c
+++ b/drivers/media/video/cx23885/cx23885-417.c
@@ -2,7 +2,7 @@
*
* Support for a cx23417 mpeg encoder via cx23885 host port.
*
- * (c) 2004 Jelle Foks <jelle@foks.8m.com>
+ * (c) 2004 Jelle Foks <jelle@foks.us>
* (c) 2004 Gerd Knorr <kraxel@bytesex.org>
* (c) 2008 Steven Toth <stoth@linuxtv.org>
* - CX23885/7/8 support
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index dac5ccc9ba72..caa098beeecf 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -158,6 +158,10 @@ struct cx23885_board cx23885_boards[] = {
.name = "Leadtek Winfast PxDVR3200 H",
.portc = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_COMPRO_VIDEOMATE_E650F] = {
+ .name = "Compro VideoMate E650F",
+ .portc = CX23885_MPEG_DVB,
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -237,6 +241,10 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x107d,
.subdevice = 0x6681,
.card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H,
+ }, {
+ .subvendor = 0x185b,
+ .subdevice = 0xe800,
+ .card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -390,6 +398,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg)
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
/* Tuner Reset Command */
bitmask = 0x04;
break;
@@ -530,6 +539,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
cx_set(GP0_IO, 0x000f000f);
break;
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
/* GPIO-2 xc3028 tuner reset */
/* The following GPIO's are on the internal AVCore (cx25840) */
@@ -630,6 +640,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1700:
case CX23885_BOARD_HAUPPAUGE_HVR1400:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
default:
ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
@@ -644,6 +655,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
case CX23885_BOARD_HAUPPAUGE_HVR1700:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
request_module("cx25840");
break;
}
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index e1aac07b3158..1c454128a9df 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -502,6 +502,7 @@ static int dvb_register(struct cx23885_tsport *port)
break;
}
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
i2c_bus = &dev->i2c_bus[0];
fe0->dvb.frontend = dvb_attach(zl10353_attach,
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index 1d53f54cd943..67828029fc69 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -66,6 +66,7 @@
#define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10
#define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11
#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12
+#define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13
/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
#define CX23885_NORMS (\
diff --git a/drivers/media/video/cx25840/Kconfig b/drivers/media/video/cx25840/Kconfig
index de515dadadc2..451133ad41ff 100644
--- a/drivers/media/video/cx25840/Kconfig
+++ b/drivers/media/video/cx25840/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_CX25840
tristate "Conexant CX2584x audio/video decoders"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Conexant CX2584x audio/video decoders.
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
index d6421e1e8f6a..d199d80ea0a3 100644
--- a/drivers/media/video/cx25840/cx25840-audio.c
+++ b/drivers/media/video/cx25840/cx25840-audio.c
@@ -25,7 +25,7 @@
static int set_audclk_freq(struct i2c_client *client, u32 freq)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
if (freq != 32000 && freq != 44100 && freq != 48000)
return -EINVAL;
@@ -193,7 +193,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
void cx25840_audio_set_path(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
/* assert soft reset */
cx25840_and_or(client, 0x810, ~0x1, 0x01);
@@ -235,7 +235,7 @@ void cx25840_audio_set_path(struct i2c_client *client)
static int get_volume(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
int vol;
if (state->unmute_volume >= 0)
@@ -252,7 +252,7 @@ static int get_volume(struct i2c_client *client)
static void set_volume(struct i2c_client *client, int volume)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
int vol;
if (state->unmute_volume >= 0) {
@@ -340,14 +340,14 @@ static void set_balance(struct i2c_client *client, int balance)
static int get_mute(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
return state->unmute_volume >= 0;
}
static void set_mute(struct i2c_client *client, int mute)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
if (mute && state->unmute_volume == -1) {
int vol = get_volume(client);
@@ -365,7 +365,7 @@ static void set_mute(struct i2c_client *client, int mute)
int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct v4l2_control *ctrl = arg;
int retval;
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 4da8cd74f00e..2ad277189da8 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -191,7 +191,7 @@ static void cx25840_work_handler(struct work_struct *work)
static void cx25840_initialize(struct i2c_client *client)
{
DEFINE_WAIT(wait);
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct workqueue_struct *q;
/* datasheet startup in numbered steps, refer to page 3-77 */
@@ -259,7 +259,7 @@ static void cx25840_initialize(struct i2c_client *client)
static void cx23885_initialize(struct i2c_client *client)
{
DEFINE_WAIT(wait);
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct workqueue_struct *q;
/* Internal Reset */
@@ -350,7 +350,7 @@ static void cx23885_initialize(struct i2c_client *client)
void cx25840_std_setup(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
v4l2_std_id std = state->std;
int hblank, hactive, burst, vblank, vactive, sc;
int vblank656, src_decimation;
@@ -497,7 +497,7 @@ void cx25840_std_setup(struct i2c_client *client)
static void input_change(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
v4l2_std_id std = state->std;
/* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */
@@ -551,7 +551,7 @@ static void input_change(struct i2c_client *client)
static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
enum cx25840_audio_input aud_input)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 is_composite = (vid_input >= CX25840_COMPOSITE1 &&
vid_input <= CX25840_COMPOSITE8);
u8 reg;
@@ -671,7 +671,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
static int set_v4lstd(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 fmt = 0; /* zero is autodetect */
u8 pal_m = 0;
@@ -720,9 +720,10 @@ static int set_v4lstd(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case CX25840_CID_ENABLE_PVR150_WORKAROUND:
@@ -786,9 +787,10 @@ static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
return 0;
}
-static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case CX25840_CID_ENABLE_PVR150_WORKAROUND:
@@ -823,21 +825,23 @@ static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
/* ----------------------------------------------------------------------- */
-static int get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int cx25840_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
switch (fmt->type) {
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
return cx25840_vbi(client, VIDIOC_G_FMT, fmt);
default:
return -EINVAL;
}
-
return 0;
}
-static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int cx25840_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
struct v4l2_pix_format *pix;
int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
int is_50Hz = !(state->std & V4L2_STD_525_60);
@@ -914,7 +918,7 @@ static void log_video_status(struct i2c_client *client)
"0xD", "0xE", "0xF"
};
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf;
u8 gen_stat1 = cx25840_read(client, 0x40d);
u8 gen_stat2 = cx25840_read(client, 0x40e);
@@ -944,7 +948,7 @@ static void log_video_status(struct i2c_client *client)
static void log_audio_status(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 download_ctl = cx25840_read(client, 0x803);
u8 mod_det_stat0 = cx25840_read(client, 0x804);
u8 mod_det_stat1 = cx25840_read(client, 0x805);
@@ -1097,21 +1101,12 @@ static void log_audio_status(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static int cx25840_command(struct i2c_client *client, unsigned int cmd,
- void *arg)
+static int cx25840_init(struct v4l2_subdev *sd, u32 val)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
- struct v4l2_tuner *vt = arg;
- struct v4l2_routing *route = arg;
-
- /* ignore these commands */
- switch (cmd) {
- case TUNER_SET_TYPE_ADDR:
- return 0;
- }
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
if (!state->is_initialized) {
- v4l_dbg(1, cx25840_debug, client, "cmd %08x triggered fw load\n", cmd);
/* initialize on first use */
state->is_initialized = 1;
if (state->is_cx25836)
@@ -1121,50 +1116,69 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
else
cx25840_initialize(client);
}
+ return 0;
+}
- switch (cmd) {
#ifdef CONFIG_VIDEO_ADV_DEBUG
- /* ioctls to allow direct access to the
- * cx25840 registers for testing */
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
+static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = cx25840_read(client, reg->reg & 0x0fff);
- else
- cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
- break;
- }
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = cx25840_read(client, reg->reg & 0x0fff);
+ return 0;
+}
+
+static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
+ return 0;
+}
#endif
- case VIDIOC_INT_DECODE_VBI_LINE:
- return cx25840_vbi(client, cmd, arg);
+static int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- return cx25840_audio(client, cmd, arg);
+ return cx25840_vbi(client, VIDIOC_INT_DECODE_VBI_LINE, vbi);
+}
+
+static int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return cx25840_audio(client, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freq);
+}
+
+static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_STREAMON:
- v4l_dbg(1, cx25840_debug, client, "enable output\n");
+ v4l_dbg(1, cx25840_debug, client, "%s output\n",
+ enable ? "enable" : "disable");
+ if (enable) {
if (state->is_cx23885) {
u8 v = (cx25840_read(client, 0x421) | 0x0b);
cx25840_write(client, 0x421, v);
} else {
cx25840_write(client, 0x115,
- state->is_cx25836 ? 0x0c : 0x8c);
+ state->is_cx25836 ? 0x0c : 0x8c);
cx25840_write(client, 0x116,
- state->is_cx25836 ? 0x04 : 0x07);
+ state->is_cx25836 ? 0x04 : 0x07);
}
- break;
-
- case VIDIOC_STREAMOFF:
- v4l_dbg(1, cx25840_debug, client, "disable output\n");
+ } else {
if (state->is_cx23885) {
u8 v = cx25840_read(client, 0x421) & ~(0x0b);
cx25840_write(client, 0x421, v);
@@ -1172,133 +1186,136 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
cx25840_write(client, 0x115, 0x00);
cx25840_write(client, 0x116, 0x00);
}
- break;
+ }
+ return 0;
+}
- case VIDIOC_LOG_STATUS:
- log_video_status(client);
- if (!state->is_cx25836)
- log_audio_status(client);
- break;
+static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ struct cx25840_state *state = to_state(sd);
- case VIDIOC_G_CTRL:
- return get_v4lctrl(client, (struct v4l2_control *)arg);
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill_std(qc);
+ default:
+ break;
+ }
+ if (state->is_cx25836)
+ return -EINVAL;
- case VIDIOC_S_CTRL:
- return set_v4lctrl(client, (struct v4l2_control *)arg);
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ return v4l2_ctrl_query_fill(qc, 0, 65535,
+ 65535 / 100, state->default_volume);
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ return v4l2_ctrl_query_fill_std(qc);
+ default:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
+static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill_std(qc);
- default:
- break;
- }
- if (state->is_cx25836)
- return -EINVAL;
+ if (state->radio == 0 && state->std == std)
+ return 0;
+ state->radio = 0;
+ state->std = std;
+ return set_v4lstd(client);
+}
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535,
- 65535 / 100, state->default_volume);
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- return v4l2_ctrl_query_fill_std(qc);
- default:
- return -EINVAL;
- }
- return -EINVAL;
- }
+static int cx25840_s_radio(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
- case VIDIOC_G_STD:
- *(v4l2_std_id *)arg = state->std;
- break;
+ state->radio = 1;
+ return 0;
+}
- case VIDIOC_S_STD:
- if (state->radio == 0 && state->std == *(v4l2_std_id *)arg)
- return 0;
- state->radio = 0;
- state->std = *(v4l2_std_id *)arg;
- return set_v4lstd(client);
+static int cx25840_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case AUDC_SET_RADIO:
- state->radio = 1;
- break;
+ return set_input(client, route->input, state->aud_input);
+}
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = state->vid_input;
- route->output = 0;
- break;
+static int cx25840_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_S_VIDEO_ROUTING:
- return set_input(client, route->input, state->aud_input);
+ if (state->is_cx25836)
+ return -EINVAL;
+ return set_input(client, state->vid_input, route->input);
+}
- case VIDIOC_INT_G_AUDIO_ROUTING:
- if (state->is_cx25836)
- return -EINVAL;
- route->input = state->aud_input;
- route->output = 0;
- break;
+static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_S_AUDIO_ROUTING:
- if (state->is_cx25836)
- return -EINVAL;
- return set_input(client, state->vid_input, route->input);
+ if (!state->is_cx25836)
+ input_change(client);
+ return 0;
+}
- case VIDIOC_S_FREQUENCY:
- if (!state->is_cx25836) {
- input_change(client);
- }
- break;
+static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 vpres = cx25840_read(client, 0x40e) & 0x20;
+ u8 mode;
+ int val = 0;
- case VIDIOC_G_TUNER:
- {
- u8 vpres = cx25840_read(client, 0x40e) & 0x20;
- u8 mode;
- int val = 0;
+ if (state->radio)
+ return 0;
- if (state->radio)
- break;
+ vt->signal = vpres ? 0xffff : 0x0;
+ if (state->is_cx25836)
+ return 0;
- vt->signal = vpres ? 0xffff : 0x0;
- if (state->is_cx25836)
- break;
+ vt->capability |=
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
- vt->capability |=
- V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
- V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+ mode = cx25840_read(client, 0x804);
- mode = cx25840_read(client, 0x804);
+ /* get rxsubchans and audmode */
+ if ((mode & 0xf) == 1)
+ val |= V4L2_TUNER_SUB_STEREO;
+ else
+ val |= V4L2_TUNER_SUB_MONO;
- /* get rxsubchans and audmode */
- if ((mode & 0xf) == 1)
- val |= V4L2_TUNER_SUB_STEREO;
- else
- val |= V4L2_TUNER_SUB_MONO;
+ if (mode == 2 || mode == 4)
+ val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- if (mode == 2 || mode == 4)
- val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ if (mode & 0x10)
+ val |= V4L2_TUNER_SUB_SAP;
- if (mode & 0x10)
- val |= V4L2_TUNER_SUB_SAP;
+ vt->rxsubchans = val;
+ vt->audmode = state->audmode;
+ return 0;
+}
- vt->rxsubchans = val;
- vt->audmode = state->audmode;
- break;
- }
+static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_S_TUNER:
- if (state->radio || state->is_cx25836)
- break;
+ if (state->radio || state->is_cx25836)
+ return 0;
- switch (vt->audmode) {
+ switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO:
/* mono -> mono
stereo -> mono
@@ -1326,41 +1343,100 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
break;
default:
return -EINVAL;
- }
- state->audmode = vt->audmode;
- break;
+ }
+ state->audmode = vt->audmode;
+ return 0;
+}
- case VIDIOC_G_FMT:
- return get_v4lfmt(client, (struct v4l2_format *)arg);
+static int cx25840_reset(struct v4l2_subdev *sd, u32 val)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_S_FMT:
- return set_v4lfmt(client, (struct v4l2_format *)arg);
+ if (state->is_cx25836)
+ cx25836_initialize(client);
+ else if (state->is_cx23885)
+ cx23885_initialize(client);
+ else
+ cx25840_initialize(client);
+ return 0;
+}
- case VIDIOC_INT_RESET:
- if (state->is_cx25836)
- cx25836_initialize(client);
- else if (state->is_cx23885)
- cx23885_initialize(client);
- else
- cx25840_initialize(client);
- break;
+static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->id, state->rev);
+ return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev);
+}
- default:
- return -EINVAL;
- }
+static int cx25840_log_status(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ log_video_status(client);
+ if (!state->is_cx25836)
+ log_audio_status(client);
return 0;
}
+static int cx25840_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops cx25840_core_ops = {
+ .log_status = cx25840_log_status,
+ .g_chip_ident = cx25840_g_chip_ident,
+ .g_ctrl = cx25840_g_ctrl,
+ .s_ctrl = cx25840_s_ctrl,
+ .queryctrl = cx25840_queryctrl,
+ .reset = cx25840_reset,
+ .init = cx25840_init,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cx25840_g_register,
+ .s_register = cx25840_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
+ .s_frequency = cx25840_s_frequency,
+ .s_std = cx25840_s_std,
+ .s_radio = cx25840_s_radio,
+ .g_tuner = cx25840_g_tuner,
+ .s_tuner = cx25840_s_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops cx25840_audio_ops = {
+ .s_clock_freq = cx25840_s_clock_freq,
+ .s_routing = cx25840_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops cx25840_video_ops = {
+ .s_routing = cx25840_s_video_routing,
+ .g_fmt = cx25840_g_fmt,
+ .s_fmt = cx25840_s_fmt,
+ .decode_vbi_line = cx25840_decode_vbi_line,
+ .s_stream = cx25840_s_stream,
+};
+
+static const struct v4l2_subdev_ops cx25840_ops = {
+ .core = &cx25840_core_ops,
+ .tuner = &cx25840_tuner_ops,
+ .audio = &cx25840_audio_ops,
+ .video = &cx25840_video_ops,
+};
+
/* ----------------------------------------------------------------------- */
static int cx25840_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct cx25840_state *state;
+ struct v4l2_subdev *sd;
u32 id;
u16 device_id;
@@ -1392,10 +1468,11 @@ static int cx25840_probe(struct i2c_client *client,
}
state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL);
- if (state == NULL) {
+ if (state == NULL)
return -ENOMEM;
- }
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
/* Note: revision '(device_id & 0x0f) == 2' was never built. The
marking skips from 0x1 == 22 to 0x3 == 23. */
v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n",
@@ -1403,7 +1480,6 @@ static int cx25840_probe(struct i2c_client *client,
(device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f),
client->addr << 1, client->adapter->name);
- i2c_set_clientdata(client, state);
state->c = client;
state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
@@ -1430,7 +1506,10 @@ static int cx25840_probe(struct i2c_client *client,
static int cx25840_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
index b87337e590b4..be0558277ca3 100644
--- a/drivers/media/video/cx25840/cx25840-core.h
+++ b/drivers/media/video/cx25840/cx25840-core.h
@@ -22,6 +22,7 @@
#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
#include <linux/i2c.h>
/* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is
@@ -34,6 +35,7 @@
struct cx25840_state {
struct i2c_client *c;
+ struct v4l2_subdev sd;
int pvr150_workaround;
int radio;
v4l2_std_id std;
@@ -53,6 +55,11 @@ struct cx25840_state {
struct work_struct fw_work; /* work entry for fw load */
};
+static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct cx25840_state, sd);
+}
+
/* ----------------------------------------------------------------------- */
/* cx25850-core.c */
int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c
index 8d489a4b9570..0b2dceb74108 100644
--- a/drivers/media/video/cx25840/cx25840-firmware.c
+++ b/drivers/media/video/cx25840/cx25840-firmware.c
@@ -91,7 +91,7 @@ static int fw_write(struct i2c_client *client, const u8 *data, int size)
int cx25840_loadfw(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
const struct firmware *fw = NULL;
u8 buffer[FWSEND];
const u8 *ptr;
diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c
index 58e6ef1c28a0..03f09b288eb8 100644
--- a/drivers/media/video/cx25840/cx25840-vbi.c
+++ b/drivers/media/video/cx25840/cx25840-vbi.c
@@ -84,7 +84,7 @@ static int decode_vps(u8 * dst, u8 * p)
int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct v4l2_format *fmt;
struct v4l2_sliced_vbi_format *svbi;
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index 06f171ab6149..66c755c116dc 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -742,7 +742,6 @@ static int __devinit snd_cx88_create(struct snd_card *card,
core = cx88_core_get(pci);
if (NULL == core) {
err = -EINVAL;
- kfree (chip);
return err;
}
@@ -812,7 +811,7 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci,
err = snd_cx88_create(card, pci, &chip);
if (err < 0)
- return (err);
+ goto error;
err = snd_cx88_pcm(chip, 0, "CX88 Digital");
if (err < 0)
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index d3ae5b4dfca7..e162a70748c5 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -3,7 +3,7 @@
* Support for a cx23416 mpeg encoder via cx2388x host port.
* "blackbird" reference design.
*
- * (c) 2004 Jelle Foks <jelle@foks.8m.com>
+ * (c) 2004 Jelle Foks <jelle@foks.us>
* (c) 2004 Gerd Knorr <kraxel@bytesex.org>
*
* (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
@@ -39,7 +39,7 @@
#include "cx88.h"
MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards");
-MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_AUTHOR("Jelle Foks <jelle@foks.us>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
static unsigned int mpegbufs = 32;
@@ -1244,8 +1244,16 @@ static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv)
* 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 */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ /* tri-state the cx22702 pins */
+ cx_set(MO_GP0_IO, 0x00000004);
+ udelay(1000);
break;
default:
err = -ENODEV;
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 5bcbb4cc7c2a..733ede34f93a 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -1237,7 +1237,6 @@ static const struct cx88_board cx88_boards[] = {
},
},
[CX88_BOARD_WINFAST_DTV2000H] = {
- /* video inputs and radio still in testing */
.name = "WinFast DTV2000 H",
.tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
.radio_type = UNSET,
@@ -1251,7 +1250,35 @@ static const struct cx88_board cx88_boards[] = {
.gpio1 = 0x00008203,
.gpio2 = 0x00017304,
.gpio3 = 0x02000000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0001d701,
+ .gpio1 = 0x0000b207,
+ .gpio2 = 0x0001d701,
+ .gpio3 = 0x02000000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE2,
+ .vmux = 2,
+ .gpio0 = 0x0001d503,
+ .gpio1 = 0x0000b207,
+ .gpio2 = 0x0001d503,
+ .gpio3 = 0x02000000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 3,
+ .gpio0 = 0x0001d701,
+ .gpio1 = 0x0000b207,
+ .gpio2 = 0x0001d701,
+ .gpio3 = 0x02000000,
}},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00015702,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00015702,
+ .gpio3 = 0x02000000,
+ },
.mpeg = CX88_MPEG_DVB,
},
[CX88_BOARD_GENIATECH_DVBS] = {
@@ -1847,6 +1874,18 @@ static const struct cx88_board cx88_boards[] = {
} },
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_TBS_8910] = {
+ .name = "TBS 8910 DVB-S",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
[CX88_BOARD_TBS_8920] = {
.name = "TBS 8920 DVB-S/S2",
.tuner_type = TUNER_ABSENT,
@@ -1859,6 +1898,18 @@ static const struct cx88_board cx88_boards[] = {
} },
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_PROF_6200] = {
+ .name = "Prof 6200 DVB-S",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
[CX88_BOARD_PROF_7300] = {
.name = "PROF 7300 DVB-S/S2",
.tuner_type = UNSET,
@@ -1871,6 +1922,18 @@ static const struct cx88_board cx88_boards[] = {
} },
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_SATTRADE_ST4200] = {
+ .name = "SATTRADE ST4200 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
};
/* ------------------------------------------------------------------ */
@@ -1897,7 +1960,11 @@ static const struct cx88_subid cx88_subids[] = {
.subvendor = PCI_VENDOR_ID_ATI,
.subdevice = 0x00f8,
.card = CX88_BOARD_ATI_WONDER_PRO,
- },{
+ }, {
+ .subvendor = PCI_VENDOR_ID_ATI,
+ .subdevice = 0x00f9,
+ .card = CX88_BOARD_ATI_WONDER_PRO,
+ }, {
.subvendor = 0x107d,
.subdevice = 0x6611,
.card = CX88_BOARD_WINFAST2000XP_EXPERT,
@@ -2257,13 +2324,25 @@ static const struct cx88_subid cx88_subids[] = {
.subdevice = 0x2011,
.card = CX88_BOARD_OMICOM_SS4_PCI,
}, {
+ .subvendor = 0x8910,
+ .subdevice = 0x8888,
+ .card = CX88_BOARD_TBS_8910,
+ }, {
.subvendor = 0x8920,
.subdevice = 0x8888,
.card = CX88_BOARD_TBS_8920,
}, {
+ .subvendor = 0xb022,
+ .subdevice = 0x3022,
+ .card = CX88_BOARD_PROF_6200,
+ }, {
.subvendor = 0xB033,
.subdevice = 0x3033,
.card = CX88_BOARD_PROF_7300,
+ }, {
+ .subvendor = 0xb200,
+ .subdevice = 0x4200,
+ .card = CX88_BOARD_SATTRADE_ST4200,
},
};
@@ -2874,8 +2953,11 @@ static void cx88_card_setup(struct cx88_core *core)
case CX88_BOARD_TEVII_S420:
case CX88_BOARD_TEVII_S460:
case CX88_BOARD_OMICOM_SS4_PCI:
+ case CX88_BOARD_TBS_8910:
case CX88_BOARD_TBS_8920:
+ case CX88_BOARD_PROF_6200:
case CX88_BOARD_PROF_7300:
+ case CX88_BOARD_SATTRADE_ST4200:
cx_write(MO_SRST_IO, 0);
msleep(100);
cx_write(MO_SRST_IO, 1);
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index 60705b08bfe8..b045874ad04f 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -844,6 +844,9 @@ static int set_tvaudio(struct cx88_core *core)
} else if (V4L2_STD_SECAM_L & norm) {
core->tvaudio = WW_L;
+ } else if ((V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) & norm) {
+ core->tvaudio = WW_BG;
+
} else if (V4L2_STD_SECAM_DK & norm) {
core->tvaudio = WW_DK;
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index 309ca5e68063..da4dd4913d9f 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -406,7 +406,7 @@ static int tevii_dvbs_set_voltage(struct dvb_frontend *fe,
cx_write(MO_GP0_IO, 0x00006060);
break;
case SEC_VOLTAGE_OFF:
- printk("LNB Voltage SEC_VOLTAGE_off\n");
+ printk("LNB Voltage SEC_VOLTAGE_off\n");
break;
}
@@ -606,7 +606,7 @@ static int dvb_register(struct cx8802_dev *dev)
/* Get the first frontend */
fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
if (!fe0)
- return -EINVAL;
+ goto frontend_detach;
/* multi-frontend gate control is undefined or defaults to fe0 */
dev->frontends.gate = 0;
@@ -653,38 +653,35 @@ static int dvb_register(struct cx8802_dev *dev)
}
break;
case CX88_BOARD_HAUPPAUGE_HVR3000:
+ /* MFE frontend 1 */
+ mfe_shared = 1;
+ dev->frontends.gate = 2;
/* DVB-S init */
fe0->dvb.frontend = dvb_attach(cx24123_attach,
- &hauppauge_novas_config,
- &dev->core->i2c_adap);
+ &hauppauge_novas_config,
+ &dev->core->i2c_adap);
if (fe0->dvb.frontend) {
- if (!dvb_attach(isl6421_attach, fe0->dvb.frontend,
- &dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) {
- dprintk( 1, "%s(): HVR3000 - DVB-S LNB Init: failed\n", __func__);
- }
- } else {
- dprintk( 1, "%s(): HVR3000 - DVB-S Init: failed\n", __func__);
+ if (!dvb_attach(isl6421_attach,
+ fe0->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
}
- /* DVB-T init */
+ /* MFE frontend 2 */
fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
- if (fe1) {
- dev->frontends.gate = 2;
- mfe_shared = 1;
- fe1->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr_config,
- &dev->core->i2c_adap);
- if (fe1->dvb.frontend) {
- fe1->dvb.frontend->id = 1;
- if(!dvb_attach(simple_tuner_attach, fe1->dvb.frontend,
- &dev->core->i2c_adap, 0x61,
- TUNER_PHILIPS_FMD1216ME_MK3)) {
- dprintk( 1, "%s(): HVR3000 - DVB-T misc Init: failed\n", __func__);
- }
- } else {
- dprintk( 1, "%s(): HVR3000 - DVB-T Init: failed\n", __func__);
- }
- } else {
- dprintk( 1, "%s(): HVR3000 - DVB-T Init: can't find frontend 2.\n", __func__);
+ if (!fe1)
+ goto frontend_detach;
+ /* DVB-T init */
+ fe1->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &dev->core->i2c_adap);
+ if (fe1->dvb.frontend) {
+ fe1->dvb.frontend->id = 1;
+ if (!dvb_attach(simple_tuner_attach,
+ fe1->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x61, TUNER_PHILIPS_FMD1216ME_MK3))
+ goto frontend_detach;
}
break;
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
@@ -998,50 +995,51 @@ static int dvb_register(struct cx8802_dev *dev)
}
break;
case CX88_BOARD_HAUPPAUGE_HVR4000:
+ /* MFE frontend 1 */
+ mfe_shared = 1;
+ dev->frontends.gate = 2;
/* DVB-S/S2 Init */
fe0->dvb.frontend = dvb_attach(cx24116_attach,
- &hauppauge_hvr4000_config,
- &dev->core->i2c_adap);
+ &hauppauge_hvr4000_config,
+ &dev->core->i2c_adap);
if (fe0->dvb.frontend) {
- if(!dvb_attach(isl6421_attach, fe0->dvb.frontend,
- &dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) {
- dprintk( 1, "%s(): HVR4000 - DVB-S LNB Init: failed\n", __func__);
- }
- } else {
- dprintk( 1, "%s(): HVR4000 - DVB-S Init: failed\n", __func__);
+ if (!dvb_attach(isl6421_attach,
+ fe0->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
}
- /* DVB-T Init */
+ /* MFE frontend 2 */
fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
- if (fe1) {
- dev->frontends.gate = 2;
- mfe_shared = 1;
- fe1->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr_config,
- &dev->core->i2c_adap);
- if (fe1->dvb.frontend) {
- fe1->dvb.frontend->id = 1;
- if(!dvb_attach(simple_tuner_attach, fe1->dvb.frontend,
- &dev->core->i2c_adap, 0x61,
- TUNER_PHILIPS_FMD1216ME_MK3)) {
- dprintk( 1, "%s(): HVR4000 - DVB-T misc Init: failed\n", __func__);
- }
- } else {
- dprintk( 1, "%s(): HVR4000 - DVB-T Init: failed\n", __func__);
- }
- } else {
- dprintk( 1, "%s(): HVR4000 - DVB-T Init: can't find frontend 2.\n", __func__);
+ if (!fe1)
+ goto frontend_detach;
+ /* DVB-T Init */
+ fe1->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &dev->core->i2c_adap);
+ if (fe1->dvb.frontend) {
+ fe1->dvb.frontend->id = 1;
+ if (!dvb_attach(simple_tuner_attach,
+ fe1->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x61, TUNER_PHILIPS_FMD1216ME_MK3))
+ goto frontend_detach;
}
break;
case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
fe0->dvb.frontend = dvb_attach(cx24116_attach,
- &hauppauge_hvr4000_config,
- &dev->core->i2c_adap);
+ &hauppauge_hvr4000_config,
+ &dev->core->i2c_adap);
if (fe0->dvb.frontend) {
- dvb_attach(isl6421_attach, fe0->dvb.frontend,
- &dev->core->i2c_adap,
- 0x08, ISL6421_DCL, 0x00);
+ if (!dvb_attach(isl6421_attach,
+ fe0->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
}
break;
+ case CX88_BOARD_PROF_6200:
+ case CX88_BOARD_TBS_8910:
case CX88_BOARD_TEVII_S420:
fe0->dvb.frontend = dvb_attach(stv0299_attach,
&tevii_tuner_sharp_config,
@@ -1070,21 +1068,18 @@ static int dvb_register(struct cx8802_dev *dev)
fe0->dvb.frontend = dvb_attach(cx24116_attach,
&tevii_s460_config,
&core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+ if (fe0->dvb.frontend != NULL)
fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
- }
break;
case CX88_BOARD_OMICOM_SS4_PCI:
case CX88_BOARD_TBS_8920:
case CX88_BOARD_PROF_7300:
+ case CX88_BOARD_SATTRADE_ST4200:
fe0->dvb.frontend = dvb_attach(cx24116_attach,
&hauppauge_hvr4000_config,
&core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+ if (fe0->dvb.frontend != NULL)
fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
- }
break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
@@ -1092,11 +1087,11 @@ static int dvb_register(struct cx8802_dev *dev)
break;
}
- if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) {
+ if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) {
printk(KERN_ERR
"%s/2: frontend initialization failed\n",
core->name);
- return -EINVAL;
+ goto frontend_detach;
}
/* define general-purpose callback pointer */
fe0->dvb.frontend->callback = cx88_tuner_callback;
@@ -1133,40 +1128,44 @@ static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv)
* 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 */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ /* enable the cx22702 pins */
cx_clear(MO_GP0_IO, 0x00000004);
udelay(1000);
break;
case CX88_BOARD_HAUPPAUGE_HVR3000:
case CX88_BOARD_HAUPPAUGE_HVR4000:
- if(core->dvbdev->frontends.active_fe_id == 1) {
- /* DVB-S/S2 Enabled */
-
- /* Toggle reset on cx22702 leaving i2c active */
- cx_write(MO_GP0_IO, (core->board.input[0].gpio0 & 0x0000ff00) | 0x00000080);
- udelay(1000);
- cx_clear(MO_GP0_IO, 0x00000080);
- udelay(50);
- cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset */
- cx_set(MO_GP0_IO, 0x00000004); /* tri-state the cx22702 pins */
- udelay(1000);
-
- cx_write(MO_SRST_IO, 1); /* Take the cx24116/cx24123 out of reset */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ switch (core->dvbdev->frontends.active_fe_id) {
+ case 1: /* DVB-S/S2 Enabled */
+ /* tri-state the cx22702 pins */
+ cx_set(MO_GP0_IO, 0x00000004);
+ /* Take the cx24116/cx24123 out of reset */
+ cx_write(MO_SRST_IO, 1);
core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */
- } else
- if (core->dvbdev->frontends.active_fe_id == 2) {
- /* DVB-T Enabled */
-
+ break;
+ case 2: /* DVB-T Enabled */
/* Put the cx24116/cx24123 into reset */
cx_write(MO_SRST_IO, 0);
-
- /* cx22702 out of reset and enable it */
- cx_set(MO_GP0_IO, 0x00000080);
+ /* enable the cx22702 pins */
cx_clear(MO_GP0_IO, 0x00000004);
core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */
- udelay(1000);
+ break;
}
+ udelay(1000);
break;
default:
@@ -1199,8 +1198,7 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv)
{
struct cx88_core *core = drv->core;
struct cx8802_dev *dev = drv->core->dvbdev;
- int err, i;
- struct videobuf_dvb_frontend *fe;
+ int err;
dprintk( 1, "%s\n", __func__);
dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
@@ -1216,31 +1214,47 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv)
/* If vp3054 isn't enabled, a stub will just return 0 */
err = vp3054_i2c_probe(dev);
if (0 != err)
- goto fail_core;
+ goto fail_probe;
/* dvb stuff */
printk(KERN_INFO "%s/2: cx2388x based DVB/ATSC card\n", core->name);
dev->ts_gen_cntrl = 0x0c;
- for (i = 1; i <= core->board.num_frontends; i++) {
- fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i);
- if (!fe) {
- printk(KERN_ERR "%s() failed to get frontend(%d)\n", __func__, i);
- continue;
+ err = -ENODEV;
+ if (core->board.num_frontends) {
+ struct videobuf_dvb_frontend *fe;
+ int i;
+
+ for (i = 1; i <= core->board.num_frontends; i++) {
+ fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i);
+ if (fe == NULL) {
+ printk(KERN_ERR "%s() failed to get frontend(%d)\n",
+ __func__, i);
+ goto fail_probe;
+ }
+ videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_TOP,
+ sizeof(struct cx88_buffer),
+ dev);
+ /* init struct videobuf_dvb */
+ fe->dvb.name = dev->core->name;
}
- videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_TOP,
- sizeof(struct cx88_buffer),
- dev);
- /* init struct videobuf_dvb */
- fe->dvb.name = dev->core->name;
+ } else {
+ /* no frontends allocated */
+ printk(KERN_ERR "%s/2 .num_frontends should be non-zero\n",
+ core->name);
+ goto fail_core;
}
err = dvb_register(dev);
- if (err != 0)
+ if (err)
+ /* frontends/adapter de-allocated in dvb_register */
printk(KERN_ERR "%s/2: dvb_register failed (err = %d)\n",
core->name, err);
+ return err;
+fail_probe:
+ videobuf_dvb_dealloc_frontends(&core->dvbdev->frontends);
fail_core:
return err;
}
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index 3ebdcd1d83f8..a04fee235db6 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -3,7 +3,7 @@
* Support for the mpeg transport stream transfers
* PCI function #2 of the cx2388x.
*
- * (c) 2004 Jelle Foks <jelle@foks.8m.com>
+ * (c) 2004 Jelle Foks <jelle@foks.us>
* (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au>
* (c) 2004 Gerd Knorr <kraxel@bytesex.org>
*
@@ -34,7 +34,7 @@
/* ------------------------------------------------------------------ */
MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards");
-MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>");
+MODULE_AUTHOR("Jelle Foks <jelle@foks.us>");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index f4240965be32..20649b25f7ba 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -53,12 +53,11 @@
/* ----------------------------------------------------------- */
/* defines and enums */
-/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
-#define CX88_NORMS (\
- V4L2_STD_NTSC_M| V4L2_STD_NTSC_M_JP| V4L2_STD_NTSC_443 | \
- V4L2_STD_PAL_BG| V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \
- V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \
- V4L2_STD_PAL_60| V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK )
+/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM/LC */
+#define CX88_NORMS (V4L2_STD_ALL \
+ & ~V4L2_STD_PAL_H \
+ & ~V4L2_STD_NTSC_M_KR \
+ & ~V4L2_STD_SECAM_LC)
#define FORMAT_FLAGS_PACKED 0x01
#define FORMAT_FLAGS_PLANAR 0x02
@@ -229,6 +228,9 @@ extern struct sram_channel cx88_sram_channels[];
#define CX88_BOARD_TEVII_S420 73
#define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74
#define CX88_BOARD_PROF_7300 75
+#define CX88_BOARD_SATTRADE_ST4200 76
+#define CX88_BOARD_TBS_8910 77
+#define CX88_BOARD_PROF_6200 78
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
index 7a8d49ef646e..15c03f0e69ad 100644
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -424,11 +424,12 @@ static int em28xx_audio_init(struct em28xx *dev)
struct snd_pcm *pcm;
struct snd_card *card;
static int devnr;
- int ret, err;
+ int err;
- if (dev->has_audio_class) {
+ if (dev->has_alsa_audio != 1) {
/* This device does not support the extension (in this case
- the device is expecting the snd-usb-audio module */
+ the device is expecting the snd-usb-audio module or
+ doesn't have analog audio support at all) */
return 0;
}
@@ -449,7 +450,12 @@ static int em28xx_audio_init(struct em28xx *dev)
}
spin_lock_init(&adev->slock);
- ret = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
+ err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture);
pcm->info_flags = 0;
pcm->private_data = dev;
@@ -461,7 +467,7 @@ static int em28xx_audio_init(struct em28xx *dev)
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
- return -ENOMEM;
+ return err;
}
adev->sndcard = card;
adev->udev = dev->udev;
@@ -475,9 +481,10 @@ static int em28xx_audio_fini(struct em28xx *dev)
if (dev == NULL)
return 0;
- if (dev->has_audio_class) {
+ if (dev->has_alsa_audio != 1) {
/* This device does not support the extension (in this case
- the device is expecting the snd-usb-audio module */
+ the device is expecting the snd-usb-audio module or
+ doesn't have analog audio support at all) */
return 0;
}
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index d65d0572403b..e776699b62f9 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -37,6 +37,8 @@
#include "em28xx.h"
+#define DRIVER_NAME "em28xx"
+
static int tuner = -1;
module_param(tuner, int, 0444);
MODULE_PARM_DESC(tuner, "tuner type");
@@ -45,122 +47,177 @@ static unsigned int disable_ir;
module_param(disable_ir, int, 0444);
MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
+static unsigned long em28xx_devused;
+
struct em28xx_hash_table {
unsigned long hash;
unsigned int model;
unsigned int tuner;
};
+/*
+ * Reset sequences for analog/digital modes
+ */
+
+/* Reset for the most [analog] boards */
+static struct em28xx_reg_seq default_analog[] = {
+ {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Reset for the most [digital] boards */
+static struct em28xx_reg_seq default_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Board Hauppauge WinTV HVR 900 analog */
+static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
+ {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10},
+ {0x05, 0xff, 0x10, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Board Hauppauge WinTV HVR 900 digital */
+static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
+ {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x04, 0x0f, 10},
+ {EM2880_R04_GPO, 0x0c, 0x0f, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
+static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
+ {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
+
+/* Board - EM2870 Kworld 355u
+ Analog - No input analog */
+
+/* Callback for the most boards */
+static struct em28xx_reg_seq default_tuner_gpio[] = {
+ {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
+ {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10},
+ {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/*
+ * Board definitions
+ */
struct em28xx_board em28xx_boards[] = {
[EM2750_BOARD_UNKNOWN] = {
.name = "Unknown EM2750/EM2751 webcam grabber",
- .vchannels = 1,
+ .xclk = EM28XX_XCLK_FREQUENCY_48MHZ,
+ .tuner_type = TUNER_ABSENT, /* This is a webcam */
.input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = 0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
} },
},
[EM2800_BOARD_UNKNOWN] = {
.name = "Unknown EM2800 video grabber",
.is_em2800 = 1,
- .vchannels = 2,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT,
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_UNKNOWN] = {
- .name = "Unknown EM2750/28xx video grabber",
- .is_em2800 = 0,
- .tuner_type = TUNER_ABSENT,
+ .name = "Unknown EM2750/28xx video grabber",
+ .tuner_type = TUNER_ABSENT,
},
[EM2750_BOARD_DLCW_130] = {
/* Beijing Huaqi Information Digital Technology Co., Ltd */
.name = "Huaqi DLCW-130",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 1,
+ .xclk = EM28XX_XCLK_FREQUENCY_48MHZ,
+ .tuner_type = TUNER_ABSENT, /* This is a webcam */
.input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = 0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
} },
},
[EM2820_BOARD_KWORLD_PVRTV2800RF] = {
.name = "Kworld PVR TV 2800 RF",
- .is_em2800 = 0,
- .vchannels = 2,
.tuner_type = TUNER_TEMIC_PAL,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_TERRATEC_CINERGY_250] = {
.name = "Terratec Cinergy 250 USB",
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_PINNACLE_USB_2] = {
.name = "Pinnacle PCTV USB 2",
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = {
.name = "Hauppauge WinTV USB 2",
- .vchannels = 3,
.tuner_type = TUNER_PHILIPS_FM1236_MK3,
.tda9887_conf = TDA9887_PRESENT |
TDA9887_PORT1_ACTIVE|
TDA9887_PORT2_ACTIVE,
.decoder = EM28XX_TVP5150,
.has_msp34xx = 1,
- /*FIXME: S-Video not tested */
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
.amux = MSP_INPUT_DEFAULT,
@@ -174,327 +231,305 @@ struct em28xx_board em28xx_boards[] = {
[EM2820_BOARD_DLINK_USB_TV] = {
.name = "D-Link DUB-T210 TV Tuner",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
- .is_em2800 = 0,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_HERCULES_SMART_TV_USB2] = {
.name = "Hercules Smart TV USB 2.0",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
+ .decoder = EM28XX_SAA711X,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = {
.name = "Pinnacle PCTV USB 2 (Philips FM1216ME)",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
- .is_em2800 = 0,
.tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_GADMEI_UTV310] = {
.name = "Gadmei UTV310",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_TNF_5335MF,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = {
.name = "Leadtek Winfast USB II Deluxe",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7114,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
- .vmux = 2,
- .amux = 0,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
- .vmux = 0,
- .amux = 1,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = 9,
- .amux = 1,
- } },
- },
- [EM2820_BOARD_PINNACLE_DVC_100] = {
- .name = "Pinnacle Dazzle DVC 100",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
- .decoder = EM28XX_SAA7113,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_VIDEOLOGY_20K14XUSB] = {
- .name = "Videology 20K14XUSB USB2.0",
+ .name = "Videology 20K14XUSB USB2.0",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 1,
- .input = { {
+ .tuner_type = TUNER_ABSENT, /* This is a webcam */
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = 0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
} },
},
[EM2821_BOARD_PROLINK_PLAYTV_USB2] = {
.name = "SIIG AVTuner-PVR/Prolink PlayTV USB 2.0",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
- .is_em2800 = 0,
.tuner_type = TUNER_LG_PAL_NEW_TAPC, /* unknown? */
.tda9887_conf = TDA9887_PRESENT, /* unknown? */
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2821_BOARD_SUPERCOMP_USB_2] = {
.name = "Supercomp USB 2.0 TV",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
- .is_em2800 = 0,
.tuner_type = TUNER_PHILIPS_FM1236_MK3,
.tda9887_conf = TDA9887_PRESENT |
TDA9887_PORT1_ACTIVE |
TDA9887_PORT2_ACTIVE,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2821_BOARD_USBGEAR_VD204] = {
- .name = "Usbgear VD204v9",
+ .name = "Usbgear VD204v9",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 2,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2860_BOARD_NETGMBH_CAM] = {
/* Beijing Huaqi Information Digital Technology Co., Ltd */
- .name = "NetGMBH Cam",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 1,
- .input = { {
+ .name = "NetGMBH Cam",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT, /* This is a webcam */
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = 0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
} },
},
[EM2860_BOARD_TYPHOON_DVD_MAKER] = {
- .name = "Typhoon DVD Maker",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 2,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .name = "Typhoon DVD Maker",
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2860_BOARD_GADMEI_UTV330] = {
.name = "Gadmei UTV330",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_TNF_5335MF,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2860_BOARD_TERRATEC_HYBRID_XS] = {
.name = "Terratec Cinergy A Hybrid XS",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2861_BOARD_KWORLD_PVRTV_300U] = {
.name = "KWorld PVRTV 300U",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2861_BOARD_YAKUMO_MOVIE_MIXER] = {
.name = "Yakumo MovieMixer",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 1,
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
.decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2861_BOARD_PLEXTOR_PX_TV100U] = {
.name = "Plextor ConvertX PX-TV100U",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_TNF_5335MF,
.tda9887_conf = TDA9887_PRESENT,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
+
+ /* Those boards with em2870 are DVB Only*/
+
[EM2870_BOARD_TERRATEC_XS] = {
.name = "Terratec Cinergy T XS",
.valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
},
[EM2870_BOARD_TERRATEC_XS_MT2060] = {
.name = "Terratec Cinergy T XS (MT2060)",
@@ -505,6 +540,7 @@ struct em28xx_board em28xx_boards[] = {
.name = "Kworld 350 U DVB-T",
.valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
},
[EM2870_BOARD_KWORLD_355U] = {
.name = "Kworld 355 U DVB-T",
@@ -514,164 +550,216 @@ struct em28xx_board em28xx_boards[] = {
.name = "Pinnacle PCTV DVB-T",
.valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_ABSENT, /* MT2060 */
+ /* djh - I have serious doubts this is right... */
+ .xclk = EM28XX_XCLK_IR_RC5_MODE |
+ EM28XX_XCLK_FREQUENCY_10MHZ,
},
[EM2870_BOARD_COMPRO_VIDEOMATE] = {
.name = "Compro, VideoMate U3",
.valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_ABSENT, /* MT2060 */
},
+
[EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = {
.name = "Terratec Hybrid XS Secam",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.has_msp34xx = 1,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
} },
},
[EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
.name = "Hauppauge WinTV HVR 900",
- .vchannels = 3,
.tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.mts_firmware = 1,
- .has_dvb = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = {
.name = "Hauppauge WinTV HVR 900 (R2)",
- .vchannels = 3,
.tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.mts_firmware = 1,
.decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850] = {
+ .name = "Hauppauge WinTV HVR 850",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = ir_codes_hauppauge_new,
+ .decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = {
.name = "Hauppauge WinTV HVR 950",
- .vchannels = 3,
- .tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.mts_firmware = 1,
- .has_12mhz_i2s = 1,
.has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = ir_codes_hauppauge_new,
.decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = {
.name = "Pinnacle PCTV HD Pro Stick",
- .vchannels = 3,
- .tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.mts_firmware = 1,
- .has_12mhz_i2s = 1,
.has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = ir_codes_pinnacle_pctv_hd,
.decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = {
.name = "AMD ATI TV Wonder HD 600",
- .vchannels = 3,
- .tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.mts_firmware = 1,
- .has_12mhz_i2s = 1,
.has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = ir_codes_ati_tv_wonder_hd_600,
.decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2880_BOARD_TERRATEC_HYBRID_XS] = {
- .name = "Terratec Hybrid XS",
- .vchannels = 3,
- .tda9887_conf = TDA9887_PRESENT,
- .tuner_type = TUNER_XC2028,
- .decoder = EM28XX_TVP5150,
+ .name = "Terratec Hybrid XS",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
.has_dvb = 1,
+ .dvb_gpio = default_analog,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
} },
},
/* maybe there's a reason behind it why Terratec sells the Hybrid XS
@@ -679,172 +767,190 @@ struct em28xx_board em28xx_boards[] = {
maybe we'll need it lateron */
[EM2880_BOARD_TERRATEC_PRODIGY_XS] = {
.name = "Terratec Prodigy XS",
- .vchannels = 3,
- .tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2820_BOARD_MSI_VOX_USB_2] = {
.name = "MSI VOX USB 2.0",
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT |
TDA9887_PORT1_ACTIVE |
TDA9887_PORT2_ACTIVE,
.max_range_640_480 = 1,
-
- .decoder = EM28XX_SAA7114,
+ .decoder = EM28XX_SAA711X,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE4,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2800_BOARD_TERRATEC_CINERGY_200] = {
.name = "Terratec Cinergy 200 USB",
.is_em2800 = 1,
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2800_BOARD_GRABBEEX_USB2800] = {
.name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder",
.is_em2800 = 1,
- .vchannels = 2,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT, /* capture only board */
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2800_BOARD_LEADTEK_WINFAST_USBII] = {
.name = "Leadtek Winfast USB II",
.is_em2800 = 1,
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2800_BOARD_KWORLD_USB2800] = {
.name = "Kworld USB2800",
.is_em2800 = 1,
- .vchannels = 3,
.tuner_type = TUNER_PHILIPS_FCV1236D,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_PINNACLE_DVC_90] = {
.name = "Pinnacle Dazzle DVC 90/DVC 100",
- .vchannels = 3,
- .tuner_type = TUNER_ABSENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .tuner_type = TUNER_ABSENT, /* capture only board */
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2800_BOARD_VGEAR_POCKETTV] = {
.name = "V-Gear PocketTV",
.is_em2800 = 1,
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2] = {
+ .name = "Pixelview PlayTV Box 4 USB 2.0",
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ .aout = EM28XX_AOUT_MONO | /* I2S */
+ EM28XX_AOUT_MASTER, /* Line out pin */
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_PROLINK_PLAYTV_USB2] = {
.name = "Pixelview Prolink PlayTV USB 2.0",
- .vchannels = 3,
+ .has_snapshot_button = 1,
.tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_YMEC_TVF_5533MF,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_LINE_IN,
+ .amux = EM28XX_AMUX_VIDEO,
+ .aout = EM28XX_AOUT_MONO | /* I2S */
+ EM28XX_AOUT_MASTER, /* Line out pin */
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
@@ -856,228 +962,252 @@ struct em28xx_board em28xx_boards[] = {
} },
},
[EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA] = {
- .name = "PointNix Intra-Oral Camera",
+ .name = "PointNix Intra-Oral Camera",
.has_snapshot_button = 1,
- .vchannels = 1,
- .tda9887_conf = TDA9887_PRESENT,
- .tuner_type = TUNER_ABSENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
} },
},
[EM2880_BOARD_MSI_DIGIVOX_AD] = {
.name = "MSI DigiVox A/D",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = em2880_msi_digivox_ad_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
} },
},
[EM2880_BOARD_MSI_DIGIVOX_AD_II] = {
.name = "MSI DigiVox A/D II",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = em2880_msi_digivox_ad_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
} },
},
[EM2880_BOARD_KWORLD_DVB_305U] = {
.name = "KWorld DVB-T 305U",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2880_BOARD_KWORLD_DVB_310U] = {
.name = "KWorld DVB-T 310U",
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.has_dvb = 1,
+ .dvb_gpio = default_digital,
.mts_firmware = 1,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_AC97_LINE_IN,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
}, { /* S-video has not been tested yet */
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_AC97_LINE_IN,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
} },
},
[EM2881_BOARD_DNT_DA2_HYBRID] = {
.name = "DNT DA2 Hybrid",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
} },
},
[EM2881_BOARD_PINNACLE_HYBRID_PRO] = {
.name = "Pinnacle Hybrid Pro",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
} },
},
[EM2882_BOARD_PINNACLE_HYBRID_PRO] = {
.name = "Pinnacle Hybrid Pro (2)",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.mts_firmware = 1,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2882_BOARD_KWORLD_VS_DVBT] = {
.name = "Kworld VS-DVB-T 323UR",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2882_BOARD_TERRATEC_HYBRID_XS] = {
.name = "Terratec Hybrid XS (em2882)",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2883_BOARD_KWORLD_HYBRID_A316] = {
.name = "Kworld PlusTV HD Hybrid 330",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
- .is_em2800 = 0,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = default_digital,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = {
.name = "Compro VideoMate ForYou/Stereo",
- .vchannels = 2,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
@@ -1101,7 +1231,7 @@ struct usb_device_id em28xx_id_table [] = {
{ USB_DEVICE(0xeb1a, 0x2820),
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2821),
- .driver_info = EM2820_BOARD_PROLINK_PLAYTV_USB2 },
+ .driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2860),
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2861),
@@ -1164,8 +1294,8 @@ struct usb_device_id em28xx_id_table [] = {
.driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
{ USB_DEVICE(0x2040, 0x651b), /* RP HVR-950 */
.driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
- { USB_DEVICE(0x2040, 0x651f), /* HCW HVR-850 */
- .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+ { USB_DEVICE(0x2040, 0x651f),
+ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 },
{ USB_DEVICE(0x0438, 0xb002),
.driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
{ USB_DEVICE(0x2001, 0xf112),
@@ -1189,78 +1319,12 @@ struct usb_device_id em28xx_id_table [] = {
MODULE_DEVICE_TABLE(usb, em28xx_id_table);
/*
- * Reset sequences for analog/digital modes
- */
-
-/* Reset for the most [analog] boards */
-static struct em28xx_reg_seq default_analog[] = {
- {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Reset for the most [digital] boards */
-static struct em28xx_reg_seq default_digital[] = {
- {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Board Hauppauge WinTV HVR 900 analog */
-static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
- {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10},
- {0x05, 0xff, 0x10, 10},
- { -1, -1, -1, -1},
-};
-
-/* Board Hauppauge WinTV HVR 900 digital */
-static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
- {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
- {EM2880_R04_GPO, 0x04, 0x0f, 10},
- {EM2880_R04_GPO, 0x0c, 0x0f, 10},
- { -1, -1, -1, -1},
-};
-
-/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
-static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
- {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
-static struct em28xx_reg_seq em2880_msi_digivox_ad_digital[] = {
- {EM28XX_R08_GPIO, 0x6a, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Board - EM2870 Kworld 355u
- Analog - No input analog */
-static struct em28xx_reg_seq em2870_kworld_355u_digital[] = {
- {EM2880_R04_GPO, 0x01, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-/* Callback for the most boards */
-static struct em28xx_reg_seq default_callback[] = {
- {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
- {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10},
- {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Callback for EM2882 TERRATEC HYBRID XS */
-static struct em28xx_reg_seq em2882_terratec_hybrid_xs_digital[] = {
- {EM28XX_R08_GPIO, 0x2e, 0xff, 6},
- {EM28XX_R08_GPIO, 0x3e, ~EM_GPIO_4, 6},
- {EM2880_R04_GPO, 0x04, 0xff, 10},
- {EM2880_R04_GPO, 0x0c, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-/*
* EEPROM hash table for devices with generic USB IDs
*/
static struct em28xx_hash_table em28xx_eeprom_hash [] = {
/* P/N: SA 60002070465 Tuner: TVF7533-MF */
{0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
+ {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF},
{0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028},
};
@@ -1282,27 +1346,26 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
if (command != XC2028_TUNER_RESET)
return 0;
- if (dev->mode == EM28XX_ANALOG_MODE)
- rc = em28xx_gpio_set(dev, dev->tun_analog_gpio);
- else
- rc = em28xx_gpio_set(dev, dev->tun_digital_gpio);
+ rc = em28xx_gpio_set(dev, dev->board.tuner_gpio);
return rc;
}
EXPORT_SYMBOL_GPL(em28xx_tuner_callback);
-static void em28xx_set_model(struct em28xx *dev)
+static void inline em28xx_set_model(struct em28xx *dev)
{
- dev->is_em2800 = em28xx_boards[dev->model].is_em2800;
- dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx;
- dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf;
- dev->decoder = em28xx_boards[dev->model].decoder;
- dev->video_inputs = em28xx_boards[dev->model].vchannels;
- dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s;
- dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480;
- dev->has_dvb = em28xx_boards[dev->model].has_dvb;
- dev->has_snapshot_button = em28xx_boards[dev->model].has_snapshot_button;
- dev->valid = em28xx_boards[dev->model].valid;
+ memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board));
+
+ /* Those are the default values for the majority of boards
+ Use those values if not specified otherwise at boards entry
+ */
+ if (!dev->board.xclk)
+ dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE |
+ EM28XX_XCLK_FREQUENCY_12MHZ;
+
+ if (!dev->board.i2c_speed)
+ dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_100_KHZ;
}
/* Since em28xx_pre_card_setup() requires a proper dev->model,
@@ -1312,205 +1375,126 @@ void em28xx_pre_card_setup(struct em28xx *dev)
{
int rc;
- rc = em28xx_read_reg(dev, EM2880_R04_GPO);
- if (rc >= 0)
- dev->reg_gpo = rc;
+ em28xx_set_model(dev);
+
+ em28xx_info("Identified as %s (card=%d)\n",
+ dev->board.name, dev->model);
+
+ /* Set the default GPO/GPIO for legacy devices */
+ dev->reg_gpo_num = EM2880_R04_GPO;
+ dev->reg_gpio_num = EM28XX_R08_GPIO;
dev->wait_after_write = 5;
+
+ /* Based on the Chip ID, set the device configuration */
rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
if (rc > 0) {
- switch (rc) {
+ dev->chip_id = rc;
+
+ switch (dev->chip_id) {
+ case CHIP_ID_EM2750:
+ em28xx_info("chip ID is em2750\n");
+ break;
+ case CHIP_ID_EM2820:
+ em28xx_info("chip ID is em2820\n");
+ break;
+ case CHIP_ID_EM2840:
+ em28xx_info("chip ID is em2840\n");
+ break;
case CHIP_ID_EM2860:
em28xx_info("chip ID is em2860\n");
break;
+ case CHIP_ID_EM2870:
+ em28xx_info("chip ID is em2870\n");
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2874:
+ em28xx_info("chip ID is em2874\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
case CHIP_ID_EM2883:
em28xx_info("chip ID is em2882/em2883\n");
dev->wait_after_write = 0;
break;
default:
- em28xx_info("em28xx chip ID = %d\n", rc);
+ em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
}
}
- em28xx_set_model(dev);
-
- /* request some modules */
- switch (dev->model) {
- case EM2880_BOARD_TERRATEC_PRODIGY_XS:
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
- case EM2860_BOARD_TERRATEC_HYBRID_XS:
- case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
- case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
- case EM2882_BOARD_PINNACLE_HYBRID_PRO:
- case EM2883_BOARD_KWORLD_HYBRID_A316:
- case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- msleep(50);
-
- /* Sets GPO/GPIO sequences for this device */
- dev->analog_gpio = hauppauge_wintv_hvr_900_analog;
- dev->digital_gpio = hauppauge_wintv_hvr_900_digital;
- dev->tun_analog_gpio = default_callback;
- dev->tun_digital_gpio = default_callback;
- break;
-
- case EM2882_BOARD_TERRATEC_HYBRID_XS:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- msleep(50);
-
- /* should be added ir_codes here */
-
- /* Sets GPO/GPIO sequences for this device */
- dev->analog_gpio = hauppauge_wintv_hvr_900_analog;
- dev->digital_gpio = hauppauge_wintv_hvr_900_digital;
- dev->tun_analog_gpio = default_callback;
- dev->tun_digital_gpio = em2882_terratec_hybrid_xs_digital;
- break;
-
- case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
- case EM2880_BOARD_TERRATEC_HYBRID_XS:
- case EM2870_BOARD_TERRATEC_XS:
- case EM2881_BOARD_PINNACLE_HYBRID_PRO:
- case EM2880_BOARD_KWORLD_DVB_310U:
- case EM2870_BOARD_KWORLD_350U:
- case EM2881_BOARD_DNT_DA2_HYBRID:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- msleep(50);
-
- /* NOTE: EM2881_DNT_DA2_HYBRID spend 140 msleep for digital
- and analog commands. If this commands doesn't work,
- add this timer. */
-
- /* Sets GPO/GPIO sequences for this device */
- dev->analog_gpio = default_analog;
- dev->digital_gpio = default_digital;
- dev->tun_analog_gpio = default_callback;
- dev->tun_digital_gpio = default_callback;
- break;
- case EM2880_BOARD_MSI_DIGIVOX_AD:
- case EM2880_BOARD_MSI_DIGIVOX_AD_II:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- msleep(50);
-
- /* Sets GPO/GPIO sequences for this device */
- dev->analog_gpio = em2880_msi_digivox_ad_analog;
- dev->digital_gpio = em2880_msi_digivox_ad_digital;
- dev->tun_analog_gpio = default_callback;
- dev->tun_digital_gpio = default_callback;
- break;
+ /* Prepopulate cached GPO register content */
+ rc = em28xx_read_reg(dev, dev->reg_gpo_num);
+ if (rc >= 0)
+ dev->reg_gpo = rc;
- case EM2750_BOARD_UNKNOWN:
- case EM2750_BOARD_DLCW_130:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x0a", 1);
- break;
+ /* Set the initial XCLK and I2C clock values based on the board
+ definition */
+ em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
+ msleep(50);
+ /* request some modules */
+ switch (dev->model) {
case EM2861_BOARD_PLEXTOR_PX_TV100U:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
/* FIXME guess */
/* Turn on analog audio output */
- em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
break;
-
case EM2861_BOARD_KWORLD_PVRTV_300U:
case EM2880_BOARD_KWORLD_DVB_305U:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x4c", 1);
- msleep(10);
- em28xx_write_regs(dev, 0x08, "\x6d", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d);
msleep(10);
- em28xx_write_regs(dev, 0x08, "\x7d", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d);
msleep(10);
break;
-
- case EM2870_BOARD_KWORLD_355U:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- msleep(50);
-
- /* Sets GPO/GPIO sequences for this device */
- dev->digital_gpio = em2870_kworld_355u_digital;
- break;
-
case EM2870_BOARD_COMPRO_VIDEOMATE:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
/* TODO: someone can do some cleanup here...
not everything's needed */
- em28xx_write_regs(dev, 0x04, "\x00", 1);
+ em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
msleep(10);
- em28xx_write_regs(dev, 0x04, "\x01", 1);
+ em28xx_write_reg(dev, EM2880_R04_GPO, 0x01);
msleep(10);
- em28xx_write_regs(dev, 0x08, "\xfd", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
mdelay(70);
- em28xx_write_regs(dev, 0x08, "\xfc", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
mdelay(70);
- em28xx_write_regs(dev, 0x08, "\xdc", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xdc);
mdelay(70);
- em28xx_write_regs(dev, 0x08, "\xfc", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
mdelay(70);
break;
-
case EM2870_BOARD_TERRATEC_XS_MT2060:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
/* this device needs some gpio writes to get the DVB-T
demod work */
- em28xx_write_regs(dev, 0x08, "\xfe", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
mdelay(70);
- em28xx_write_regs(dev, 0x08, "\xde", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
mdelay(70);
- dev->em28xx_write_regs(dev, 0x08, "\xfe", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
mdelay(70);
break;
-
case EM2870_BOARD_PINNACLE_PCTV_DVB:
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
/* this device needs some gpio writes to get the
DVB-T demod work */
- em28xx_write_regs(dev, 0x08, "\xfe", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
mdelay(70);
- em28xx_write_regs(dev, 0x08, "\xde", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
mdelay(70);
- em28xx_write_regs(dev, 0x08, "\xfe", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
mdelay(70);
- /* switch em2880 rc protocol */
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x22", 1);
- /* should be added ir_codes here */
break;
-
case EM2820_BOARD_GADMEI_UTV310:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- /* Turn on analog audio output */
- em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1);
- break;
-
- case EM2860_BOARD_GADMEI_UTV330:
- /* Turn on IR */
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- /* should be added ir_codes here */
- break;
-
case EM2820_BOARD_MSI_VOX_USB_2:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- /* enables audio for that device */
- em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1);
+ /* enables audio for that devices */
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
break;
}
- em28xx_gpio_set(dev, dev->tun_analog_gpio);
+ em28xx_gpio_set(dev, dev->board.tuner_gpio);
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
/* Unlock device */
- em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
}
static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
@@ -1536,6 +1520,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
ctl->demod = XC3028_FE_DEFAULT;
ctl->fname = XC3028L_DEFAULT_FIRMWARE;
break;
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
/* FIXME: Better to specify the needed IF */
@@ -1712,12 +1697,15 @@ void em28xx_card_setup(struct em28xx *dev)
em28xx_set_model(dev);
dev->tuner_type = em28xx_boards[dev->model].tuner_type;
+ if (em28xx_boards[dev->model].tuner_addr)
+ dev->tuner_addr = em28xx_boards[dev->model].tuner_addr;
/* request some modules */
switch (dev->model) {
case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
{
struct tveeprom tv;
@@ -1733,7 +1721,7 @@ void em28xx_card_setup(struct em28xx *dev)
if (tv.audio_processor == V4L2_IDENT_MSPX4XX) {
dev->i2s_speed = 2048000;
- dev->has_msp34xx = 1;
+ dev->board.has_msp34xx = 1;
}
#ifdef CONFIG_MODULES
if (tv.has_ir)
@@ -1743,7 +1731,7 @@ void em28xx_card_setup(struct em28xx *dev)
}
case EM2820_BOARD_KWORLD_PVRTV2800RF:
/* GPIO enables sound on KWORLD PVR TV 2800RF */
- em28xx_write_regs_req(dev, 0x00, 0x08, "\xf9", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9);
break;
case EM2820_BOARD_UNKNOWN:
case EM2800_BOARD_UNKNOWN:
@@ -1766,10 +1754,10 @@ void em28xx_card_setup(struct em28xx *dev)
break;
}
- if (dev->has_snapshot_button)
+ if (dev->board.has_snapshot_button)
em28xx_register_snapshot_button(dev);
- if (dev->valid == EM28XX_BOARD_NOT_VALIDATED) {
+ if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) {
em28xx_errdev("\n\n");
em28xx_errdev("The support for this board weren't "
"valid yet.\n");
@@ -1784,15 +1772,433 @@ void em28xx_card_setup(struct em28xx *dev)
#ifdef CONFIG_MODULES
/* request some modules */
- if (dev->has_msp34xx)
+ if (dev->board.has_msp34xx)
request_module("msp3400");
- if (dev->decoder == EM28XX_SAA7113 || dev->decoder == EM28XX_SAA7114)
+ if (dev->board.decoder == EM28XX_SAA711X)
request_module("saa7115");
- if (dev->decoder == EM28XX_TVP5150)
+ if (dev->board.decoder == EM28XX_TVP5150)
request_module("tvp5150");
- if (dev->tuner_type != TUNER_ABSENT)
+ if (dev->board.tuner_type != TUNER_ABSENT)
request_module("tuner");
#endif
em28xx_config_tuner(dev);
+
+ em28xx_ir_init(dev);
+}
+
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct em28xx *dev = container_of(work,
+ struct em28xx, request_module_wk);
+
+ if (dev->has_audio_class)
+ request_module("snd-usb-audio");
+ else if (dev->has_alsa_audio)
+ request_module("em28xx-alsa");
+
+ if (dev->board.has_dvb)
+ request_module("em28xx-dvb");
+}
+
+static void request_modules(struct em28xx *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+/*
+ * em28xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+void em28xx_release_resources(struct em28xx *dev)
+{
+ if (dev->sbutton_input_dev)
+ em28xx_deregister_snapshot_button(dev);
+
+ if (dev->ir)
+ em28xx_ir_fini(dev);
+
+ /*FIXME: I2C IR should be disconnected */
+
+ em28xx_release_analog_resources(dev);
+
+ em28xx_remove_from_devlist(dev);
+
+ em28xx_i2c_unregister(dev);
+ usb_put_dev(dev->udev);
+
+ /* Mark device as unused */
+ em28xx_devused &= ~(1 << dev->devno);
+};
+
+/*
+ * em28xx_init_dev()
+ * allocates and inits the device structs, registers i2c bus and v4l device
+ */
+int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
+ int minor)
+{
+ struct em28xx *dev = *devhandle;
+ int retval = -ENOMEM;
+ int errCode;
+
+ dev->udev = udev;
+ mutex_init(&dev->ctrl_urb_lock);
+ spin_lock_init(&dev->slock);
+ init_waitqueue_head(&dev->open);
+ init_waitqueue_head(&dev->wait_frame);
+ init_waitqueue_head(&dev->wait_stream);
+
+ dev->em28xx_write_regs = em28xx_write_regs;
+ dev->em28xx_read_reg = em28xx_read_reg;
+ dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
+ dev->em28xx_write_regs_req = em28xx_write_regs_req;
+ dev->em28xx_read_reg_req = em28xx_read_reg_req;
+ dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
+
+ em28xx_pre_card_setup(dev);
+
+ if (!dev->board.is_em2800) {
+ /* Sets I2C speed to 100 KHz */
+ retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_regs_req failed!"
+ " retval [%d]\n",
+ __func__, retval);
+ return retval;
+ }
+ }
+
+ /* register i2c bus */
+ errCode = em28xx_i2c_register(dev);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+
+ /* Do board specific init and eeprom reading */
+ em28xx_card_setup(dev);
+
+ /* Configure audio */
+ errCode = em28xx_audio_setup(dev);
+ if (errCode < 0) {
+ em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n",
+ __func__, errCode);
+ }
+
+ /* wake i2c devices */
+ em28xx_wake_i2c(dev);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+
+
+ if (dev->board.has_msp34xx) {
+ /* Send a reset to other chips via gpio */
+ errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_write_regs_req - "
+ "msp34xx(1) failed! errCode [%d]\n",
+ __func__, errCode);
+ return errCode;
+ }
+ msleep(3);
+
+ errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_write_regs_req - "
+ "msp34xx(2) failed! errCode [%d]\n",
+ __func__, errCode);
+ return errCode;
+ }
+ msleep(3);
+ }
+
+ em28xx_add_into_devlist(dev);
+
+ retval = em28xx_register_analog_devices(dev);
+ if (retval < 0) {
+ em28xx_release_resources(dev);
+ goto fail_reg_devices;
+ }
+
+ em28xx_init_extension(dev);
+
+ /* Save some power by putting tuner to sleep */
+ em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
+
+ return 0;
+
+fail_reg_devices:
+ return retval;
+}
+
+/*
+ * em28xx_usb_probe()
+ * checks for supported devices
+ */
+static int em28xx_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ const struct usb_endpoint_descriptor *endpoint;
+ struct usb_device *udev;
+ struct usb_interface *uif;
+ struct em28xx *dev = NULL;
+ int retval = -ENODEV;
+ int i, nr, ifnum, isoc_pipe;
+ char *speed;
+ char descr[255] = "";
+
+ udev = usb_get_dev(interface_to_usbdev(interface));
+ ifnum = interface->altsetting[0].desc.bInterfaceNumber;
+
+ /* Check to see next free device and mark as used */
+ nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
+ em28xx_devused |= 1<<nr;
+
+ /* Don't register audio interfaces */
+ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ em28xx_err(DRIVER_NAME " audio device (%04x:%04x): "
+ "interface %i, class %i\n",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting[0].desc.bInterfaceClass);
+
+ em28xx_devused &= ~(1<<nr);
+ return -ENODEV;
+ }
+
+ endpoint = &interface->cur_altsetting->endpoint[0].desc;
+
+ /* check if the device has the iso in endpoint at the correct place */
+ if (usb_endpoint_xfer_isoc(endpoint)
+ &&
+ (interface->altsetting[1].endpoint[0].desc.wMaxPacketSize == 940)) {
+ /* It's a newer em2874/em2875 device */
+ isoc_pipe = 0;
+ } else {
+ int check_interface = 1;
+ isoc_pipe = 1;
+ endpoint = &interface->cur_altsetting->endpoint[1].desc;
+ if (usb_endpoint_type(endpoint) !=
+ USB_ENDPOINT_XFER_ISOC)
+ check_interface = 0;
+
+ if (usb_endpoint_dir_out(endpoint))
+ check_interface = 0;
+
+ if (!check_interface) {
+ em28xx_err(DRIVER_NAME " video device (%04x:%04x): "
+ "interface %i, class %i found.\n",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting[0].desc.bInterfaceClass);
+
+ em28xx_err(DRIVER_NAME " This is an anciliary "
+ "interface not used by the driver\n");
+
+ em28xx_devused &= ~(1<<nr);
+ return -ENODEV;
+ }
+ }
+
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ speed = "1.5";
+ break;
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_FULL:
+ speed = "12";
+ break;
+ case USB_SPEED_HIGH:
+ speed = "480";
+ break;
+ default:
+ speed = "unknown";
+ }
+
+ if (udev->manufacturer)
+ strlcpy(descr, udev->manufacturer, sizeof(descr));
+
+ if (udev->product) {
+ if (*descr)
+ strlcat(descr, " ", sizeof(descr));
+ strlcat(descr, udev->product, sizeof(descr));
+ }
+ if (*descr)
+ strlcat(descr, " ", sizeof(descr));
+
+ printk(DRIVER_NAME ": New device %s@ %s Mbps "
+ "(%04x:%04x, interface %d, class %d)\n",
+ descr,
+ speed,
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting->desc.bInterfaceNumber);
+
+ if (nr >= EM28XX_MAXBOARDS) {
+ printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
+ EM28XX_MAXBOARDS);
+ em28xx_devused &= ~(1<<nr);
+ return -ENOMEM;
+ }
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ em28xx_err(DRIVER_NAME ": out of memory!\n");
+ em28xx_devused &= ~(1<<nr);
+ return -ENOMEM;
+ }
+
+ snprintf(dev->name, 29, "em28xx #%d", nr);
+ dev->devno = nr;
+ dev->model = id->driver_info;
+ dev->alt = -1;
+
+ /* Checks if audio is provided by some interface */
+ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
+ uif = udev->config->interface[i];
+ if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ dev->has_audio_class = 1;
+ break;
+ }
+ }
+
+ /* compute alternate max packet sizes */
+ uif = udev->actconfig->interface[0];
+
+ dev->num_alt = uif->num_altsetting;
+ dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL);
+
+ if (dev->alt_max_pkt_size == NULL) {
+ em28xx_errdev("out of memory!\n");
+ em28xx_devused &= ~(1<<nr);
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < dev->num_alt ; i++) {
+ u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
+ dev->alt_max_pkt_size[i] =
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+ }
+
+ if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
+ dev->model = card[nr];
+
+ /* allocate device struct */
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
+ retval = em28xx_init_dev(&dev, udev, nr);
+ if (retval) {
+ em28xx_devused &= ~(1<<dev->devno);
+ kfree(dev);
+
+ return retval;
+ }
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, dev);
+
+ request_modules(dev);
+
+ /* Should be the last thing to do, to avoid newer udev's to
+ open the device before fully initializing it
+ */
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+/*
+ * em28xx_usb_disconnect()
+ * called when the device gets diconencted
+ * video device will be unregistered on v4l2_close in case it is still open
+ */
+static void em28xx_usb_disconnect(struct usb_interface *interface)
+{
+ struct em28xx *dev;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ if (!dev)
+ return;
+
+ em28xx_info("disconnecting %s\n", dev->vdev->name);
+
+ /* wait until all current v4l2 io is finished then deallocate
+ resources */
+ mutex_lock(&dev->lock);
+
+ wake_up_interruptible_all(&dev->open);
+
+ if (dev->users) {
+ em28xx_warn
+ ("device /dev/video%d is open! Deregistration and memory "
+ "deallocation are deferred on close.\n",
+ dev->vdev->num);
+
+ dev->state |= DEV_MISCONFIGURED;
+ em28xx_uninit_isoc(dev);
+ dev->state |= DEV_DISCONNECTED;
+ wake_up_interruptible(&dev->wait_frame);
+ wake_up_interruptible(&dev->wait_stream);
+ } else {
+ dev->state |= DEV_DISCONNECTED;
+ em28xx_release_resources(dev);
+ }
+
+ em28xx_close_extension(dev);
+
+ mutex_unlock(&dev->lock);
+
+ if (!dev->users) {
+ kfree(dev->alt_max_pkt_size);
+ kfree(dev);
+ }
}
+
+static struct usb_driver em28xx_usb_driver = {
+ .name = "em28xx",
+ .probe = em28xx_usb_probe,
+ .disconnect = em28xx_usb_disconnect,
+ .id_table = em28xx_id_table,
+};
+
+static int __init em28xx_module_init(void)
+{
+ int result;
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&em28xx_usb_driver);
+ if (result)
+ em28xx_err(DRIVER_NAME
+ " usb_register failed. Error number %d.\n", result);
+
+ printk(KERN_INFO DRIVER_NAME " driver loaded\n");
+
+ return result;
+}
+
+static void __exit em28xx_module_exit(void)
+{
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&em28xx_usb_driver);
+}
+
+module_init(em28xx_module_init);
+module_exit(em28xx_module_exit);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 15e2b525310d..f8504518586a 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/vmalloc.h>
+#include <media/v4l2-common.h>
#include "em28xx.h"
@@ -66,7 +67,8 @@ MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
char *buf, int len)
{
- int ret, byte;
+ int ret;
+ int pipe = usb_rcvctrlpipe(dev->udev, 0);
if (dev->state & DEV_DISCONNECTED)
return -ENODEV;
@@ -74,10 +76,18 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
if (len > URB_MAX_CTRL_SIZE)
return -EINVAL;
- em28xx_regdbg("req=%02x, reg=%02x ", req, reg);
+ if (reg_debug) {
+ printk( KERN_DEBUG "(pipe 0x%08x): "
+ "IN: %02x %02x %02x %02x %02x %02x %02x %02x ",
+ pipe,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, 0,
+ reg & 0xff, reg >> 8,
+ len & 0xff, len >> 8);
+ }
mutex_lock(&dev->ctrl_urb_lock);
- ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), req,
+ ret = usb_control_msg(dev->udev, pipe, req,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x0000, reg, dev->urb_buf, len, HZ);
if (ret < 0) {
@@ -93,7 +103,9 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
mutex_unlock(&dev->ctrl_urb_lock);
if (reg_debug) {
- printk("%02x values: ", ret);
+ int byte;
+
+ printk("<<<");
for (byte = 0; byte < len; byte++)
printk(" %02x", (unsigned char)buf[byte]);
printk("\n");
@@ -108,28 +120,12 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
*/
int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg)
{
- u8 val;
int ret;
+ u8 val;
- if (dev->state & DEV_DISCONNECTED)
- return(-ENODEV);
-
- em28xx_regdbg("req=%02x, reg=%02x:", req, reg);
-
- mutex_lock(&dev->ctrl_urb_lock);
- ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), req,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0x0000, reg, dev->urb_buf, 1, HZ);
- val = dev->urb_buf[0];
- mutex_unlock(&dev->ctrl_urb_lock);
-
- if (ret < 0) {
- printk(" failed!\n");
+ ret = em28xx_read_reg_req_len(dev, req, reg, &val, 1);
+ if (ret < 0)
return ret;
- }
-
- if (reg_debug)
- printk("%02x\n", (unsigned char) val);
return val;
}
@@ -147,6 +143,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
int len)
{
int ret;
+ int pipe = usb_sndctrlpipe(dev->udev, 0);
if (dev->state & DEV_DISCONNECTED)
return -ENODEV;
@@ -154,17 +151,25 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
if ((len < 1) || (len > URB_MAX_CTRL_SIZE))
return -EINVAL;
- em28xx_regdbg("req=%02x reg=%02x:", req, reg);
if (reg_debug) {
- int i;
- for (i = 0; i < len; ++i)
- printk(" %02x", (unsigned char)buf[i]);
+ int byte;
+
+ printk( KERN_DEBUG "(pipe 0x%08x): "
+ "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>",
+ pipe,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, 0,
+ reg & 0xff, reg >> 8,
+ len & 0xff, len >> 8);
+
+ for (byte = 0; byte < len; byte++)
+ printk(" %02x", (unsigned char)buf[byte]);
printk("\n");
}
mutex_lock(&dev->ctrl_urb_lock);
memcpy(dev->urb_buf, buf, len);
- ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), req,
+ ret = usb_control_msg(dev->udev, pipe, req,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x0000, reg, dev->urb_buf, len, HZ);
mutex_unlock(&dev->ctrl_urb_lock);
@@ -187,15 +192,21 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
Not sure what happens on reading GPO register.
*/
if (rc >= 0) {
- if (reg == EM2880_R04_GPO)
+ if (reg == dev->reg_gpo_num)
dev->reg_gpo = buf[0];
- else if (reg == EM28XX_R08_GPIO)
+ else if (reg == dev->reg_gpio_num)
dev->reg_gpio = buf[0];
}
return rc;
}
+/* Write a single register */
+int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val)
+{
+ return em28xx_write_regs(dev, reg, &val, 1);
+}
+
/*
* em28xx_write_reg_bits()
* sets only some bits (specified by bitmask) of a register, by first reading
@@ -208,9 +219,9 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
u8 newval;
/* Uses cache for gpo/gpio registers */
- if (reg == EM2880_R04_GPO)
+ if (reg == dev->reg_gpo_num)
oldval = dev->reg_gpo;
- else if (reg == EM28XX_R08_GPIO)
+ else if (reg == dev->reg_gpio_num)
oldval = dev->reg_gpio;
else
oldval = em28xx_read_reg(dev, reg);
@@ -224,15 +235,70 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
}
/*
+ * em28xx_is_ac97_ready()
+ * Checks if ac97 is ready
+ */
+static int em28xx_is_ac97_ready(struct em28xx *dev)
+{
+ int ret, i;
+
+ /* Wait up to 50 ms for AC97 command to complete */
+ for (i = 0; i < 10; i++, msleep(5)) {
+ ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & 0x01))
+ return 0;
+ }
+
+ em28xx_warn("AC97 command still being executed: not handled properly!\n");
+ return -EBUSY;
+}
+
+/*
+ * em28xx_read_ac97()
+ * write a 16 bit value to the specified AC97 address (LSB first!)
+ */
+int em28xx_read_ac97(struct em28xx *dev, u8 reg)
+{
+ int ret;
+ u8 addr = (reg & 0x7f) | 0x80;
+ u16 val;
+
+ ret = em28xx_is_ac97_ready(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R40_AC97LSB,
+ (u8 *)&val, sizeof(val));
+
+ if (ret < 0)
+ return ret;
+ return le16_to_cpu(val);
+}
+
+/*
* em28xx_write_ac97()
* write a 16 bit value to the specified AC97 address (LSB first!)
*/
-static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val)
+int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val)
{
- int ret, i;
+ int ret;
u8 addr = reg & 0x7f;
+ __le16 value;
- ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, val, 2);
+ value = cpu_to_le16(val);
+
+ ret = em28xx_is_ac97_ready(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *) &value, 2);
if (ret < 0)
return ret;
@@ -240,58 +306,74 @@ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val)
if (ret < 0)
return ret;
- /* Wait up to 50 ms for AC97 command to complete */
- for (i = 0; i < 10; i++) {
- ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY);
- if (ret < 0)
- return ret;
+ return 0;
+}
- if (!(ret & 0x01))
- return 0;
- msleep(5);
+struct em28xx_vol_table {
+ enum em28xx_amux mux;
+ u8 reg;
+};
+
+static struct em28xx_vol_table inputs[] = {
+ { EM28XX_AMUX_VIDEO, AC97_VIDEO_VOL },
+ { EM28XX_AMUX_LINE_IN, AC97_LINEIN_VOL },
+ { EM28XX_AMUX_PHONE, AC97_PHONE_VOL },
+ { EM28XX_AMUX_MIC, AC97_MIC_VOL },
+ { EM28XX_AMUX_CD, AC97_CD_VOL },
+ { EM28XX_AMUX_AUX, AC97_AUX_VOL },
+ { EM28XX_AMUX_PCM_OUT, AC97_PCM_OUT_VOL },
+};
+
+static int set_ac97_input(struct em28xx *dev)
+{
+ int ret, i;
+ enum em28xx_amux amux = dev->ctl_ainput;
+
+ /* EM28XX_AMUX_VIDEO2 is a special case used to indicate that
+ em28xx should point to LINE IN, while AC97 should use VIDEO
+ */
+ if (amux == EM28XX_AMUX_VIDEO2)
+ amux = EM28XX_AMUX_VIDEO;
+
+ /* Mute all entres but the one that were selected */
+ for (i = 0; i < ARRAY_SIZE(inputs); i++) {
+ if (amux == inputs[i].mux)
+ ret = em28xx_write_ac97(dev, inputs[i].reg, 0x0808);
+ else
+ ret = em28xx_write_ac97(dev, inputs[i].reg, 0x8000);
+
+ if (ret < 0)
+ em28xx_warn("couldn't setup AC97 register %d\n",
+ inputs[i].reg);
}
- em28xx_warn("AC97 command still being executed: not handled properly!\n");
return 0;
}
static int em28xx_set_audio_source(struct em28xx *dev)
{
- static char *enable = "\x08\x08";
- static char *disable = "\x08\x88";
- char *video = enable, *line = disable;
int ret;
u8 input;
- if (dev->is_em2800) {
- if (dev->ctl_ainput)
- input = EM2800_AUDIO_SRC_LINE;
- else
+ if (dev->board.is_em2800) {
+ if (dev->ctl_ainput == EM28XX_AMUX_VIDEO)
input = EM2800_AUDIO_SRC_TUNER;
+ else
+ input = EM2800_AUDIO_SRC_LINE;
ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1);
if (ret < 0)
return ret;
}
- if (dev->has_msp34xx)
+ if (dev->board.has_msp34xx)
input = EM28XX_AUDIO_SRC_TUNER;
else {
switch (dev->ctl_ainput) {
case EM28XX_AMUX_VIDEO:
input = EM28XX_AUDIO_SRC_TUNER;
break;
- case EM28XX_AMUX_LINE_IN:
+ default:
input = EM28XX_AUDIO_SRC_LINE;
- video = disable;
- line = enable;
- break;
- case EM28XX_AMUX_AC97_VIDEO:
- input = EM28XX_AUDIO_SRC_LINE;
- break;
- case EM28XX_AMUX_AC97_LINE_IN:
- input = EM28XX_AUDIO_SRC_LINE;
- video = disable;
- line = enable;
break;
}
}
@@ -301,41 +383,50 @@ static int em28xx_set_audio_source(struct em28xx *dev)
return ret;
msleep(5);
- /* Sets AC97 mixer registers
- This is seems to be needed, even for non-ac97 configs
- */
- ret = em28xx_write_ac97(dev, EM28XX_R14_VIDEO_AC97, video);
- if (ret < 0)
- return ret;
-
- ret = em28xx_write_ac97(dev, EM28XX_R10_LINE_IN_AC97, line);
+ switch (dev->audio_mode.ac97) {
+ case EM28XX_NO_AC97:
+ break;
+ default:
+ ret = set_ac97_input(dev);
+ }
return ret;
}
+struct em28xx_vol_table outputs[] = {
+ { EM28XX_AOUT_MASTER, AC97_MASTER_VOL },
+ { EM28XX_AOUT_LINE, AC97_LINE_LEVEL_VOL },
+ { EM28XX_AOUT_MONO, AC97_MASTER_MONO_VOL },
+ { EM28XX_AOUT_LFE, AC97_LFE_MASTER_VOL },
+ { EM28XX_AOUT_SURR, AC97_SURR_MASTER_VOL },
+};
+
int em28xx_audio_analog_set(struct em28xx *dev)
{
- int ret;
- char s[2] = { 0x00, 0x00 };
- u8 xclk = 0x07;
-
- s[0] |= 0x1f - dev->volume;
- s[1] |= 0x1f - dev->volume;
-
- /* Mute */
- s[1] |= 0x80;
- ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s);
+ int ret, i;
+ u8 xclk;
- if (ret < 0)
- return ret;
+ if (!dev->audio_mode.has_audio)
+ return 0;
- if (dev->has_12mhz_i2s)
- xclk |= 0x20;
+ /* It is assumed that all devices use master volume for output.
+ It would be possible to use also line output.
+ */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ /* Mute all outputs */
+ for (i = 0; i < ARRAY_SIZE(outputs); i++) {
+ ret = em28xx_write_ac97(dev, outputs[i].reg, 0x8000);
+ if (ret < 0)
+ em28xx_warn("couldn't setup AC97 register %d\n",
+ outputs[i].reg);
+ }
+ }
+ xclk = dev->board.xclk & 0x7f;
if (!dev->mute)
xclk |= 0x80;
- ret = em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, xclk, 0xa7);
+ ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk);
if (ret < 0)
return ret;
msleep(10);
@@ -343,36 +434,169 @@ int em28xx_audio_analog_set(struct em28xx *dev)
/* Selects the proper audio input */
ret = em28xx_set_audio_source(dev);
- /* Unmute device */
- if (!dev->mute)
- s[1] &= ~0x80;
- ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s);
+ /* Sets volume */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ int vol;
+
+ /* LSB: left channel - both channels with the same level */
+ vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8);
+
+ /* Mute device, if needed */
+ if (dev->mute)
+ vol |= 0x8000;
+
+ /* Sets volume */
+ for (i = 0; i < ARRAY_SIZE(outputs); i++) {
+ if (dev->ctl_aoutput & outputs[i].mux)
+ ret = em28xx_write_ac97(dev, outputs[i].reg,
+ vol);
+ if (ret < 0)
+ em28xx_warn("couldn't setup AC97 register %d\n",
+ outputs[i].reg);
+ }
+ }
return ret;
}
EXPORT_SYMBOL_GPL(em28xx_audio_analog_set);
-int em28xx_colorlevels_set_default(struct em28xx *dev)
+int em28xx_audio_setup(struct em28xx *dev)
{
- em28xx_write_regs(dev, EM28XX_R20_YGAIN, "\x10", 1); /* contrast */
- em28xx_write_regs(dev, EM28XX_R21_YOFFSET, "\x00", 1); /* brightness */
- em28xx_write_regs(dev, EM28XX_R22_UVGAIN, "\x10", 1); /* saturation */
- em28xx_write_regs(dev, EM28XX_R23_UOFFSET, "\x00", 1);
- em28xx_write_regs(dev, EM28XX_R24_VOFFSET, "\x00", 1);
- em28xx_write_regs(dev, EM28XX_R25_SHARPNESS, "\x00", 1);
+ int vid1, vid2, feat, cfg;
+ u32 vid;
+
+ if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874) {
+ /* Digital only device - don't load any alsa module */
+ dev->audio_mode.has_audio = 0;
+ dev->has_audio_class = 0;
+ dev->has_alsa_audio = 0;
+ return 0;
+ }
+
+ /* If device doesn't support Usb Audio Class, use vendor class */
+ if (!dev->has_audio_class)
+ dev->has_alsa_audio = 1;
+
+ dev->audio_mode.has_audio = 1;
- em28xx_write_regs(dev, EM28XX_R14_GAMMA, "\x20", 1);
- em28xx_write_regs(dev, EM28XX_R15_RGAIN, "\x20", 1);
- em28xx_write_regs(dev, EM28XX_R16_GGAIN, "\x20", 1);
- em28xx_write_regs(dev, EM28XX_R17_BGAIN, "\x20", 1);
- em28xx_write_regs(dev, EM28XX_R18_ROFFSET, "\x00", 1);
- em28xx_write_regs(dev, EM28XX_R19_GOFFSET, "\x00", 1);
- return em28xx_write_regs(dev, EM28XX_R1A_BOFFSET, "\x00", 1);
+ /* See how this device is configured */
+ cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG);
+ if (cfg < 0)
+ cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */
+ else
+ em28xx_info("Config register raw data: 0x%02x\n", cfg);
+
+ if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
+ EM28XX_CHIPCFG_I2S_3_SAMPRATES) {
+ em28xx_info("I2S Audio (3 sample rates)\n");
+ dev->audio_mode.i2s_3rates = 1;
+ }
+ if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
+ EM28XX_CHIPCFG_I2S_5_SAMPRATES) {
+ em28xx_info("I2S Audio (5 sample rates)\n");
+ dev->audio_mode.i2s_5rates = 1;
+ }
+
+ if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) {
+ /* Skip the code that does AC97 vendor detection */
+ dev->audio_mode.ac97 = EM28XX_NO_AC97;
+ goto init_audio;
+ }
+
+ dev->audio_mode.ac97 = EM28XX_AC97_OTHER;
+
+ vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1);
+ if (vid1 < 0) {
+ /* Device likely doesn't support AC97 */
+ em28xx_warn("AC97 chip type couldn't be determined\n");
+ goto init_audio;
+ }
+
+ vid2 = em28xx_read_ac97(dev, AC97_VENDOR_ID2);
+ if (vid2 < 0)
+ goto init_audio;
+
+ vid = vid1 << 16 | vid2;
+
+ dev->audio_mode.ac97_vendor_id = vid;
+ em28xx_warn("AC97 vendor ID = 0x%08x\n", vid);
+
+ feat = em28xx_read_ac97(dev, AC97_RESET);
+ if (feat < 0)
+ goto init_audio;
+
+ dev->audio_mode.ac97_feat = feat;
+ em28xx_warn("AC97 features = 0x%04x\n", feat);
+
+ /* Try to identify what audio processor we have */
+ if ((vid == 0xffffffff) && (feat == 0x6a90))
+ dev->audio_mode.ac97 = EM28XX_AC97_EM202;
+ else if ((vid >> 8) == 0x838476)
+ dev->audio_mode.ac97 = EM28XX_AC97_SIGMATEL;
+
+init_audio:
+ /* Reports detected AC97 processor */
+ switch (dev->audio_mode.ac97) {
+ case EM28XX_NO_AC97:
+ em28xx_info("No AC97 audio processor\n");
+ break;
+ case EM28XX_AC97_EM202:
+ em28xx_info("Empia 202 AC97 audio processor detected\n");
+ break;
+ case EM28XX_AC97_SIGMATEL:
+ em28xx_info("Sigmatel audio processor detected(stac 97%02x)\n",
+ dev->audio_mode.ac97_vendor_id & 0xff);
+ break;
+ case EM28XX_AC97_OTHER:
+ em28xx_warn("Unknown AC97 audio processor detected!\n");
+ break;
+ default:
+ break;
+ }
+
+ return em28xx_audio_analog_set(dev);
+}
+EXPORT_SYMBOL_GPL(em28xx_audio_setup);
+
+int em28xx_colorlevels_set_default(struct em28xx *dev)
+{
+ em28xx_write_reg(dev, EM28XX_R20_YGAIN, 0x10); /* contrast */
+ em28xx_write_reg(dev, EM28XX_R21_YOFFSET, 0x00); /* brightness */
+ em28xx_write_reg(dev, EM28XX_R22_UVGAIN, 0x10); /* saturation */
+ em28xx_write_reg(dev, EM28XX_R23_UOFFSET, 0x00);
+ em28xx_write_reg(dev, EM28XX_R24_VOFFSET, 0x00);
+ em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, 0x00);
+
+ em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20);
+ em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20);
+ em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20);
+ em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20);
+ em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00);
+ em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00);
+ return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00);
}
int em28xx_capture_start(struct em28xx *dev, int start)
{
int rc;
+
+ if (dev->chip_id == CHIP_ID_EM2874) {
+ /* The Transport Stream Enable Register moved in em2874 */
+ if (!start) {
+ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+ 0x00,
+ EM2874_TS1_CAPTURE_ENABLE);
+ return rc;
+ }
+
+ /* Enable Transport Stream */
+ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+ EM2874_TS1_CAPTURE_ENABLE,
+ EM2874_TS1_CAPTURE_ENABLE);
+ return rc;
+ }
+
+
/* FIXME: which is the best order? */
/* video registers are sampled by VREF */
rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP,
@@ -382,28 +606,37 @@ int em28xx_capture_start(struct em28xx *dev, int start)
if (!start) {
/* disable video capture */
- rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x27", 1);
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27);
return rc;
}
/* enable video capture */
- rc = em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
+ rc = em28xx_write_reg(dev, 0x48, 0x00);
if (dev->mode == EM28XX_ANALOG_MODE)
- rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x67", 1);
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
else
- rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x37", 1);
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
msleep(6);
return rc;
}
-int em28xx_outfmt_set_yuv422(struct em28xx *dev)
+int em28xx_set_outfmt(struct em28xx *dev)
{
- em28xx_write_regs(dev, EM28XX_R27_OUTFMT, "\x34", 1);
- em28xx_write_regs(dev, EM28XX_R10_VINMODE, "\x10", 1);
- return em28xx_write_regs(dev, EM28XX_R11_VINCTRL, "\x11", 1);
+ int ret;
+
+ ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT,
+ dev->format->reg | 0x20, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, 0x10);
+ if (ret < 0)
+ return ret;
+
+ return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x11);
}
static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax,
@@ -440,7 +673,7 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v)
{
u8 mode;
/* the em2800 scaler only supports scaling down to 50% */
- if (dev->is_em2800)
+ if (dev->board.is_em2800)
mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00);
else {
u8 buf[2];
@@ -464,7 +697,7 @@ int em28xx_resolution_set(struct em28xx *dev)
width = norm_maxw(dev);
height = norm_maxh(dev) >> 1;
- em28xx_outfmt_set_yuv422(dev);
+ em28xx_set_outfmt(dev);
em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2);
em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2);
return em28xx_scaler_set(dev, dev->hscale, dev->vscale);
@@ -519,12 +752,14 @@ int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio)
if (!gpio)
return rc;
- dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
- if (dev->mode == EM28XX_ANALOG_MODE)
- dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1);
- else
- dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x37", 1);
- msleep(6);
+ if (dev->mode != EM28XX_SUSPEND) {
+ em28xx_write_reg(dev, 0x48, 0x00);
+ if (dev->mode == EM28XX_ANALOG_MODE)
+ em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
+ else
+ em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
+ msleep(6);
+ }
/* Send GPIO reset sequences specified at board entry */
while (gpio->sleep >= 0) {
@@ -549,17 +784,20 @@ int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode)
if (dev->mode == set_mode)
return 0;
- if (set_mode == EM28XX_MODE_UNDEFINED) {
+ if (set_mode == EM28XX_SUSPEND) {
dev->mode = set_mode;
- return 0;
+
+ /* FIXME: add suspend support for ac97 */
+
+ return em28xx_gpio_set(dev, dev->board.suspend_gpio);
}
dev->mode = set_mode;
if (dev->mode == EM28XX_DIGITAL_MODE)
- return em28xx_gpio_set(dev, dev->digital_gpio);
+ return em28xx_gpio_set(dev, dev->board.dvb_gpio);
else
- return em28xx_gpio_set(dev, dev->analog_gpio);
+ return em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio);
}
EXPORT_SYMBOL_GPL(em28xx_set_mode);
@@ -738,3 +976,145 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
return 0;
}
EXPORT_SYMBOL_GPL(em28xx_init_isoc);
+
+/*
+ * em28xx_wake_i2c()
+ * configure i2c attached devices
+ */
+void em28xx_wake_i2c(struct em28xx *dev)
+{
+ struct v4l2_routing route;
+ int zero = 0;
+
+ route.input = INPUT(dev->ctl_input)->vmux;
+ route.output = 0;
+ em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero);
+ em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
+}
+
+/*
+ * Device control list
+ */
+
+static LIST_HEAD(em28xx_devlist);
+static DEFINE_MUTEX(em28xx_devlist_mutex);
+
+struct em28xx *em28xx_get_device(struct inode *inode,
+ enum v4l2_buf_type *fh_type,
+ int *has_radio)
+{
+ struct em28xx *h, *dev = NULL;
+ int minor = iminor(inode);
+
+ *fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ *has_radio = 0;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(h, &em28xx_devlist, devlist) {
+ if (h->vdev->minor == minor)
+ dev = h;
+ if (h->vbi_dev->minor == minor) {
+ dev = h;
+ *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ }
+ if (h->radio_dev &&
+ h->radio_dev->minor == minor) {
+ dev = h;
+ *has_radio = 1;
+ }
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+
+ return dev;
+}
+
+/*
+ * em28xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+void em28xx_remove_from_devlist(struct em28xx *dev)
+{
+ mutex_lock(&em28xx_devlist_mutex);
+ list_del(&dev->devlist);
+ mutex_unlock(&em28xx_devlist_mutex);
+};
+
+void em28xx_add_into_devlist(struct em28xx *dev)
+{
+ mutex_lock(&em28xx_devlist_mutex);
+ list_add_tail(&dev->devlist, &em28xx_devlist);
+ mutex_unlock(&em28xx_devlist_mutex);
+};
+
+/*
+ * Extension interface
+ */
+
+static LIST_HEAD(em28xx_extension_devlist);
+static DEFINE_MUTEX(em28xx_extension_devlist_lock);
+
+int em28xx_register_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *dev = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ mutex_lock(&em28xx_extension_devlist_lock);
+ list_add_tail(&ops->next, &em28xx_extension_devlist);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+ if (dev)
+ ops->init(dev);
+ }
+ printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
+ mutex_unlock(&em28xx_extension_devlist_lock);
+ mutex_unlock(&em28xx_devlist_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(em28xx_register_extension);
+
+void em28xx_unregister_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *dev = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+ if (dev)
+ ops->fini(dev);
+ }
+
+ mutex_lock(&em28xx_extension_devlist_lock);
+ printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
+ list_del(&ops->next);
+ mutex_unlock(&em28xx_extension_devlist_lock);
+ mutex_unlock(&em28xx_devlist_mutex);
+}
+EXPORT_SYMBOL(em28xx_unregister_extension);
+
+void em28xx_init_extension(struct em28xx *dev)
+{
+ struct em28xx_ops *ops = NULL;
+
+ mutex_lock(&em28xx_extension_devlist_lock);
+ if (!list_empty(&em28xx_extension_devlist)) {
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->init)
+ ops->init(dev);
+ }
+ }
+ mutex_unlock(&em28xx_extension_devlist_lock);
+}
+
+void em28xx_close_extension(struct em28xx *dev)
+{
+ struct em28xx_ops *ops = NULL;
+
+ mutex_lock(&em28xx_extension_devlist_lock);
+ if (!list_empty(&em28xx_extension_devlist)) {
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->fini)
+ ops->fini(dev);
+ }
+ }
+ mutex_unlock(&em28xx_extension_devlist_lock);
+}
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index c99e2383b7ec..d38cb21834d9 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -161,7 +161,7 @@ static int stop_streaming(struct em28xx_dvb *dvb)
em28xx_uninit_isoc(dev);
- em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
return 0;
}
@@ -215,7 +215,7 @@ static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
if (acquire)
return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
else
- return em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ return em28xx_set_mode(dev, EM28XX_SUSPEND);
}
/* ------------------------------------------------------------------ */
@@ -393,7 +393,7 @@ static int dvb_init(struct em28xx *dev)
int result = 0;
struct em28xx_dvb *dvb;
- if (!dev->has_dvb) {
+ if (!dev->board.has_dvb) {
/* This device does not support the extension */
return 0;
}
@@ -409,8 +409,10 @@ static int dvb_init(struct em28xx *dev)
em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
/* init frontend */
switch (dev->model) {
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
+ case EM2883_BOARD_KWORLD_HYBRID_A316:
case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
dvb->frontend = dvb_attach(lgdt330x_attach,
&em2880_lgdt3303_dev,
@@ -466,12 +468,12 @@ static int dvb_init(struct em28xx *dev)
if (result < 0)
goto out_free;
- em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
printk(KERN_INFO "Successfully loaded em28xx-dvb\n");
return 0;
out_free:
- em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
kfree(dvb);
dev->dvb = NULL;
return result;
@@ -479,7 +481,7 @@ out_free:
static int dvb_fini(struct em28xx *dev)
{
- if (!dev->has_dvb) {
+ if (!dev->board.has_dvb) {
/* This device does not support the extension */
return 0;
}
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index 2360c61ddca9..d69f0efcc9aa 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -250,7 +250,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
(msgs[i].flags & I2C_M_RD) ? "read" : "write",
i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
if (!msgs[i].len) { /* no len: check only for device presence */
- if (dev->is_em2800)
+ if (dev->board.is_em2800)
rc = em2800_i2c_check_for_device(dev, addr);
else
rc = em28xx_i2c_check_for_device(dev, addr);
@@ -261,7 +261,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
} else if (msgs[i].flags & I2C_M_RD) {
/* read bytes */
- if (dev->is_em2800)
+ if (dev->board.is_em2800)
rc = em2800_i2c_recv_bytes(dev, addr,
msgs[i].buf,
msgs[i].len);
@@ -279,7 +279,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
for (byte = 0; byte < msgs[i].len; byte++)
printk(" %02x", msgs[i].buf[byte]);
}
- if (dev->is_em2800)
+ if (dev->board.is_em2800)
rc = em2800_i2c_send_bytes(dev, addr,
msgs[i].buf,
msgs[i].len);
@@ -332,6 +332,17 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
struct em28xx_eeprom *em_eeprom = (void *)eedata;
int i, err, size = len, block;
+ if (dev->chip_id == CHIP_ID_EM2874) {
+ /* Empia switched to a 16-bit addressable eeprom in newer
+ devices. While we could certainly write a routine to read
+ the eeprom, there is nothing of use in there that cannot be
+ accessed through registers, and there is the risk that we
+ could corrupt the eeprom (since a 16-bit read call is
+ interpreted as a write call by 8-bit eeproms).
+ */
+ return 0;
+ }
+
dev->i2c_client.addr = 0xa0 >> 1;
/* Check if board has eeprom */
@@ -377,47 +388,49 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
if (em_eeprom->id == 0x9567eb1a)
dev->hash = em28xx_hash_mem(eedata, len, 32);
- printk(KERN_INFO "EEPROM ID= 0x%08x, hash = 0x%08lx\n",
- em_eeprom->id, dev->hash);
- printk(KERN_INFO "Vendor/Product ID= %04x:%04x\n", em_eeprom->vendor_ID,
- em_eeprom->product_ID);
+ printk(KERN_INFO "%s: EEPROM ID= 0x%08x, EEPROM hash = 0x%08lx\n",
+ dev->name, em_eeprom->id, dev->hash);
+
+ printk(KERN_INFO "%s: EEPROM info:\n", dev->name);
switch (em_eeprom->chip_conf >> 4 & 0x3) {
case 0:
- printk(KERN_INFO "No audio on board.\n");
+ printk(KERN_INFO "%s:\tNo audio on board.\n", dev->name);
break;
case 1:
- printk(KERN_INFO "AC97 audio (5 sample rates)\n");
+ printk(KERN_INFO "%s:\tAC97 audio (5 sample rates)\n",
+ dev->name);
break;
case 2:
- printk(KERN_INFO "I2S audio, sample rate=32k\n");
+ printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n", dev->name);
break;
case 3:
- printk(KERN_INFO "I2S audio, 3 sample rates\n");
+ printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n", dev->name);
break;
}
if (em_eeprom->chip_conf & 1 << 3)
- printk(KERN_INFO "USB Remote wakeup capable\n");
+ printk(KERN_INFO "%s:\tUSB Remote wakeup capable\n", dev->name);
if (em_eeprom->chip_conf & 1 << 2)
- printk(KERN_INFO "USB Self power capable\n");
+ printk(KERN_INFO "%s:\tUSB Self power capable\n", dev->name);
switch (em_eeprom->chip_conf & 0x3) {
case 0:
- printk(KERN_INFO "500mA max power\n");
+ printk(KERN_INFO "%s:\t500mA max power\n", dev->name);
break;
case 1:
- printk(KERN_INFO "400mA max power\n");
+ printk(KERN_INFO "%s:\t400mA max power\n", dev->name);
break;
case 2:
- printk(KERN_INFO "300mA max power\n");
+ printk(KERN_INFO "%s:\t300mA max power\n", dev->name);
break;
case 3:
- printk(KERN_INFO "200mA max power\n");
+ printk(KERN_INFO "%s:\t200mA max power\n", dev->name);
break;
}
- printk(KERN_INFO "Table at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n",
+ printk(KERN_INFO "%s:\tTable at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n",
+ dev->name,
em_eeprom->string_idx_table,
em_eeprom->string1,
em_eeprom->string2,
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index eab3d9511af3..42bbaf64aceb 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -38,12 +38,48 @@ static unsigned int ir_debug;
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
-#define dprintk(fmt, arg...) \
+#define i2cdprintk(fmt, arg...) \
if (ir_debug) { \
printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \
}
-/* ----------------------------------------------------------------------- */
+#define dprintk(fmt, arg...) \
+ if (ir_debug) { \
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
+ }
+
+/**********************************************************
+ Polling structure used by em28xx IR's
+ **********************************************************/
+
+struct em28xx_ir_poll_result {
+ unsigned int toggle_bit:1;
+ unsigned int read_count:7;
+ u8 rc_address;
+ u8 rc_data[4]; /* 1 byte on em2860/2880, 4 on em2874 */
+};
+
+struct em28xx_IR {
+ struct em28xx *dev;
+ struct input_dev *input;
+ struct ir_input_state ir;
+ char name[32];
+ char phys[32];
+
+ /* poll external decoder */
+ int polling;
+ struct work_struct work;
+ struct timer_list timer;
+ unsigned int last_toggle:1;
+ unsigned int last_readcount;
+ unsigned int repeat_interval;
+
+ int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *);
+};
+
+/**********************************************************
+ I2C IR based get keycodes - should be used with ir-kbd-i2c
+ **********************************************************/
int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
@@ -51,7 +87,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
/* poll IR chip */
if (1 != i2c_master_recv(&ir->c, &b, 1)) {
- dprintk("read error\n");
+ i2cdprintk("read error\n");
return -EIO;
}
@@ -59,7 +95,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
down, while 0xff indicates that no button is hold
down. 0xfe sequences are sometimes interrupted by 0xFF */
- dprintk("key %02x\n", b);
+ i2cdprintk("key %02x\n", b);
if (b == 0xff)
return 0;
@@ -73,7 +109,6 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
return 1;
}
-
int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
unsigned char buf[2];
@@ -97,7 +132,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
((buf[0]&0x10)>>3) | /* 0000 0010 */
((buf[0]&0x20)>>5); /* 0000 0001 */
- dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",
+ i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",
code, buf[0]);
/* return key */
@@ -114,11 +149,11 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
/* poll IR chip */
if (3 != i2c_master_recv(&ir->c, buf, 3)) {
- dprintk("read error\n");
+ i2cdprintk("read error\n");
return -EIO;
}
- dprintk("key %02x\n", buf[2]&0x3f);
+ i2cdprintk("key %02x\n", buf[2]&0x3f);
if (buf[0] != 0x00)
return 0;
@@ -128,6 +163,260 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
return 1;
}
+/**********************************************************
+ Poll based get keycode functions
+ **********************************************************/
+
+/* This is for the em2860/em2880 */
+static int default_polling_getkey(struct em28xx_IR *ir,
+ struct em28xx_ir_poll_result *poll_result)
+{
+ struct em28xx *dev = ir->dev;
+ int rc;
+ u8 msg[3] = { 0, 0, 0 };
+
+ /* Read key toggle, brand, and key code
+ on registers 0x45, 0x46 and 0x47
+ */
+ rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR,
+ msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ /* Infrared toggle (Reg 0x45[7]) */
+ poll_result->toggle_bit = (msg[0] >> 7);
+
+ /* Infrared read count (Reg 0x45[6:0] */
+ poll_result->read_count = (msg[0] & 0x7f);
+
+ /* Remote Control Address (Reg 0x46) */
+ poll_result->rc_address = msg[1];
+
+ /* Remote Control Data (Reg 0x47) */
+ poll_result->rc_data[0] = msg[2];
+
+ return 0;
+}
+
+static int em2874_polling_getkey(struct em28xx_IR *ir,
+ struct em28xx_ir_poll_result *poll_result)
+{
+ struct em28xx *dev = ir->dev;
+ int rc;
+ u8 msg[5] = { 0, 0, 0, 0, 0 };
+
+ /* Read key toggle, brand, and key code
+ on registers 0x51-55
+ */
+ rc = dev->em28xx_read_reg_req_len(dev, 0, EM2874_R51_IR,
+ msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ /* Infrared toggle (Reg 0x51[7]) */
+ poll_result->toggle_bit = (msg[0] >> 7);
+
+ /* Infrared read count (Reg 0x51[6:0] */
+ poll_result->read_count = (msg[0] & 0x7f);
+
+ /* Remote Control Address (Reg 0x52) */
+ poll_result->rc_address = msg[1];
+
+ /* Remote Control Data (Reg 0x53-55) */
+ poll_result->rc_data[0] = msg[2];
+ poll_result->rc_data[1] = msg[3];
+ poll_result->rc_data[2] = msg[4];
+
+ return 0;
+}
+
+/**********************************************************
+ Polling code for em28xx
+ **********************************************************/
+
+static void em28xx_ir_handle_key(struct em28xx_IR *ir)
+{
+ int result;
+ int do_sendkey = 0;
+ struct em28xx_ir_poll_result poll_result;
+
+ /* read the registers containing the IR status */
+ result = ir->get_key(ir, &poll_result);
+ if (result < 0) {
+ dprintk("ir->get_key() failed %d\n", result);
+ return;
+ }
+
+ dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n",
+ poll_result.toggle_bit, poll_result.read_count,
+ ir->last_readcount, poll_result.rc_data[0]);
+
+ if (ir->dev->chip_id == CHIP_ID_EM2874) {
+ /* The em2874 clears the readcount field every time the
+ register is read. The em2860/2880 datasheet says that it
+ is supposed to clear the readcount, but it doesn't. So with
+ the em2874, we are looking for a non-zero read count as
+ opposed to a readcount that is incrementing */
+ ir->last_readcount = 0;
+ }
+
+ if (poll_result.read_count == 0) {
+ /* The button has not been pressed since the last read */
+ } else if (ir->last_toggle != poll_result.toggle_bit) {
+ /* A button has been pressed */
+ dprintk("button has been pressed\n");
+ ir->last_toggle = poll_result.toggle_bit;
+ ir->repeat_interval = 0;
+ do_sendkey = 1;
+ } else if (poll_result.toggle_bit == ir->last_toggle &&
+ poll_result.read_count > 0 &&
+ poll_result.read_count != ir->last_readcount) {
+ /* The button is still being held down */
+ dprintk("button being held down\n");
+
+ /* Debouncer for first keypress */
+ if (ir->repeat_interval++ > 9) {
+ /* Start repeating after 1 second */
+ do_sendkey = 1;
+ }
+ }
+
+ if (do_sendkey) {
+ dprintk("sending keypress\n");
+ ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0],
+ poll_result.rc_data[0]);
+ ir_input_nokey(ir->input, &ir->ir);
+ }
+
+ ir->last_readcount = poll_result.read_count;
+ return;
+}
+
+static void ir_timer(unsigned long data)
+{
+ struct em28xx_IR *ir = (struct em28xx_IR *)data;
+
+ schedule_work(&ir->work);
+}
+
+static void em28xx_ir_work(struct work_struct *work)
+{
+ struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work);
+
+ em28xx_ir_handle_key(ir);
+ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
+}
+
+void em28xx_ir_start(struct em28xx_IR *ir)
+{
+ setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
+ INIT_WORK(&ir->work, em28xx_ir_work);
+ schedule_work(&ir->work);
+}
+
+static void em28xx_ir_stop(struct em28xx_IR *ir)
+{
+ del_timer_sync(&ir->timer);
+ flush_scheduled_work();
+}
+
+int em28xx_ir_init(struct em28xx *dev)
+{
+ struct em28xx_IR *ir;
+ struct input_dev *input_dev;
+ u8 ir_config;
+ int err = -ENOMEM;
+
+ if (dev->board.ir_codes == NULL) {
+ /* No remote control support */
+ return 0;
+ }
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ir || !input_dev)
+ goto err_out_free;
+
+ ir->input = input_dev;
+
+ /* Setup the proper handler based on the chip */
+ switch (dev->chip_id) {
+ case CHIP_ID_EM2860:
+ case CHIP_ID_EM2883:
+ ir->get_key = default_polling_getkey;
+ break;
+ case CHIP_ID_EM2874:
+ ir->get_key = em2874_polling_getkey;
+ /* For now we only support RC5, so enable it */
+ ir_config = EM2874_IR_RC5;
+ em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1);
+ break;
+ default:
+ printk("Unrecognized em28xx chip id: IR not supported\n");
+ goto err_out_free;
+ }
+
+ /* This is how often we ask the chip for IR information */
+ ir->polling = 100; /* ms */
+
+ /* init input device */
+ snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)",
+ dev->name);
+
+ usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
+ strlcat(ir->phys, "/input0", sizeof(ir->phys));
+
+ ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes);
+ input_dev->name = ir->name;
+ input_dev->phys = ir->phys;
+ input_dev->id.bustype = BUS_USB;
+ input_dev->id.version = 1;
+ input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+ input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+
+ input_dev->dev.parent = &dev->udev->dev;
+ /* record handles to ourself */
+ ir->dev = dev;
+ dev->ir = ir;
+
+ em28xx_ir_start(ir);
+
+ /* all done */
+ err = input_register_device(ir->input);
+ if (err)
+ goto err_out_stop;
+
+ return 0;
+ err_out_stop:
+ em28xx_ir_stop(ir);
+ dev->ir = NULL;
+ err_out_free:
+ input_free_device(input_dev);
+ kfree(ir);
+ return err;
+}
+
+int em28xx_ir_fini(struct em28xx *dev)
+{
+ struct em28xx_IR *ir = dev->ir;
+
+ /* skip detach on non attached boards */
+ if (!ir)
+ return 0;
+
+ em28xx_ir_stop(ir);
+ input_unregister_device(ir->input);
+ kfree(ir);
+
+ /* done */
+ dev->ir = NULL;
+ return 0;
+}
+
+/**********************************************************
+ Handle Webcam snapshot button
+ **********************************************************/
+
static void em28xx_query_sbutton(struct work_struct *work)
{
/* Poll the register and see if the button is depressed */
@@ -210,9 +499,3 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev)
}
return;
}
-
-/* ----------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
index fac1ab23f621..65dcb91bdcc2 100644
--- a/drivers/media/video/em28xx/em28xx-reg.h
+++ b/drivers/media/video/em28xx/em28xx-reg.h
@@ -17,17 +17,58 @@
/* em28xx registers */
+#define EM28XX_R00_CHIPCFG 0x00
+
+/* em28xx Chip Configuration 0x00 */
+#define EM28XX_CHIPCFG_VENDOR_AUDIO 0x80
+#define EM28XX_CHIPCFG_I2S_VOLUME_CAPABLE 0x40
+#define EM28XX_CHIPCFG_I2S_5_SAMPRATES 0x30
+#define EM28XX_CHIPCFG_I2S_3_SAMPRATES 0x20
+#define EM28XX_CHIPCFG_AC97 0x10
+#define EM28XX_CHIPCFG_AUDIOMASK 0x30
+
/* GPIO/GPO registers */
#define EM2880_R04_GPO 0x04 /* em2880-em2883 only */
#define EM28XX_R08_GPIO 0x08 /* em2820 or upper */
#define EM28XX_R06_I2C_CLK 0x06
+
+/* em28xx I2C Clock Register (0x06) */
+#define EM28XX_I2C_CLK_ACK_LAST_READ 0x80
+#define EM28XX_I2C_CLK_WAIT_ENABLE 0x40
+#define EM28XX_I2C_EEPROM_ON_BOARD 0x08
+#define EM28XX_I2C_EEPROM_KEY_VALID 0x04
+#define EM2874_I2C_SECONDARY_BUS_SELECT 0x04 /* em2874 has two i2c busses */
+#define EM28XX_I2C_FREQ_1_5_MHZ 0x03 /* bus frequency (bits [1-0]) */
+#define EM28XX_I2C_FREQ_25_KHZ 0x02
+#define EM28XX_I2C_FREQ_400_KHZ 0x01
+#define EM28XX_I2C_FREQ_100_KHZ 0x00
+
+
#define EM28XX_R0A_CHIPID 0x0a
#define EM28XX_R0C_USBSUSP 0x0c /* */
#define EM28XX_R0E_AUDIOSRC 0x0e
#define EM28XX_R0F_XCLK 0x0f
+/* em28xx XCLK Register (0x0f) */
+#define EM28XX_XCLK_AUDIO_UNMUTE 0x80 /* otherwise audio muted */
+#define EM28XX_XCLK_I2S_MSB_TIMING 0x40 /* otherwise standard timing */
+#define EM28XX_XCLK_IR_RC5_MODE 0x20 /* otherwise NEC mode */
+#define EM28XX_XCLK_IR_NEC_CHK_PARITY 0x10
+#define EM28XX_XCLK_FREQUENCY_30MHZ 0x00 /* Freq. select (bits [3-0]) */
+#define EM28XX_XCLK_FREQUENCY_15MHZ 0x01
+#define EM28XX_XCLK_FREQUENCY_10MHZ 0x02
+#define EM28XX_XCLK_FREQUENCY_7_5MHZ 0x03
+#define EM28XX_XCLK_FREQUENCY_6MHZ 0x04
+#define EM28XX_XCLK_FREQUENCY_5MHZ 0x05
+#define EM28XX_XCLK_FREQUENCY_4_3MHZ 0x06
+#define EM28XX_XCLK_FREQUENCY_12MHZ 0x07
+#define EM28XX_XCLK_FREQUENCY_20MHZ 0x08
+#define EM28XX_XCLK_FREQUENCY_20MHZ_2 0x09
+#define EM28XX_XCLK_FREQUENCY_48MHZ 0x0a
+#define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b
+
#define EM28XX_R10_VINMODE 0x10
#define EM28XX_R11_VINCTRL 0x11
#define EM28XX_R12_VINENABLE 0x12 /* */
@@ -56,6 +97,19 @@
#define EM28XX_R26_COMPR 0x26
#define EM28XX_R27_OUTFMT 0x27
+/* em28xx Output Format Register (0x27) */
+#define EM28XX_OUTFMT_RGB_8_RGRG 0x00
+#define EM28XX_OUTFMT_RGB_8_GRGR 0x01
+#define EM28XX_OUTFMT_RGB_8_GBGB 0x02
+#define EM28XX_OUTFMT_RGB_8_BGBG 0x03
+#define EM28XX_OUTFMT_RGB_16_656 0x04
+#define EM28XX_OUTFMT_RGB_8_BAYER 0x08 /* Pattern in Reg 0x10[1-0] */
+#define EM28XX_OUTFMT_YUV211 0x10
+#define EM28XX_OUTFMT_YUV422_Y0UY1V 0x14
+#define EM28XX_OUTFMT_YUV422_Y1UY0V 0x15
+#define EM28XX_OUTFMT_YUV411 0x18
+
+
#define EM28XX_R28_XMIN 0x28
#define EM28XX_R29_XMAX 0x29
#define EM28XX_R2A_YMIN 0x2a
@@ -71,10 +125,32 @@
#define EM28XX_R42_AC97ADDR 0x42
#define EM28XX_R43_AC97BUSY 0x43
-/* em202 registers */
-#define EM28XX_R02_MASTER_AC97 0x02
-#define EM28XX_R10_LINE_IN_AC97 0x10
-#define EM28XX_R14_VIDEO_AC97 0x14
+#define EM28XX_R45_IR 0x45
+ /* 0x45 bit 7 - parity bit
+ bits 6-0 - count
+ 0x46 IR brand
+ 0x47 IR data
+ */
+
+/* em2874 registers */
+#define EM2874_R50_IR_CONFIG 0x50
+#define EM2874_R51_IR 0x51
+#define EM2874_R5F_TS_ENABLE 0x5f
+#define EM2874_R80_GPIO 0x80
+
+/* em2874 IR config register (0x50) */
+#define EM2874_IR_NEC 0x00
+#define EM2874_IR_RC5 0x04
+#define EM2874_IR_RC5_MODE_0 0x08
+#define EM2874_IR_RC5_MODE_6A 0x0b
+
+/* em2874 Transport Stream Enable Register (0x5f) */
+#define EM2874_TS1_CAPTURE_ENABLE (1 << 0)
+#define EM2874_TS1_FILTER_ENABLE (1 << 1)
+#define EM2874_TS1_NULL_DISCARD (1 << 2)
+#define EM2874_TS2_CAPTURE_ENABLE (1 << 4)
+#define EM2874_TS2_FILTER_ENABLE (1 << 5)
+#define EM2874_TS2_NULL_DISCARD (1 << 6)
/* register settings */
#define EM2800_AUDIO_SRC_TUNER 0x0d
@@ -84,6 +160,75 @@
/* FIXME: Need to be populated with the other chip ID's */
enum em28xx_chip_id {
+ CHIP_ID_EM2820 = 18,
+ CHIP_ID_EM2840 = 20,
+ CHIP_ID_EM2750 = 33,
CHIP_ID_EM2860 = 34,
+ CHIP_ID_EM2870 = 35,
CHIP_ID_EM2883 = 36,
+ CHIP_ID_EM2874 = 65,
};
+
+/*
+ * Registers used by em202 and other AC97 chips
+ */
+
+/* Standard AC97 registers */
+#define AC97_RESET 0x00
+
+ /* Output volumes */
+#define AC97_MASTER_VOL 0x02
+#define AC97_LINE_LEVEL_VOL 0x04 /* Some devices use for headphones */
+#define AC97_MASTER_MONO_VOL 0x06
+
+ /* Input volumes */
+#define AC97_PC_BEEP_VOL 0x0a
+#define AC97_PHONE_VOL 0x0c
+#define AC97_MIC_VOL 0x0e
+#define AC97_LINEIN_VOL 0x10
+#define AC97_CD_VOL 0x12
+#define AC97_VIDEO_VOL 0x14
+#define AC97_AUX_VOL 0x16
+#define AC97_PCM_OUT_VOL 0x18
+
+ /* capture registers */
+#define AC97_RECORD_SELECT 0x1a
+#define AC97_RECORD_GAIN 0x1c
+
+ /* control registers */
+#define AC97_GENERAL_PURPOSE 0x20
+#define AC97_3D_CTRL 0x22
+#define AC97_AUD_INT_AND_PAG 0x24
+#define AC97_POWER_DOWN_CTRL 0x26
+#define AC97_EXT_AUD_ID 0x28
+#define AC97_EXT_AUD_CTRL 0x2a
+
+/* Supported rate varies for each AC97 device
+ if write an unsupported value, it will return the closest one
+ */
+#define AC97_PCM_OUT_FRONT_SRATE 0x2c
+#define AC97_PCM_OUT_SURR_SRATE 0x2e
+#define AC97_PCM_OUT_LFE_SRATE 0x30
+#define AC97_PCM_IN_SRATE 0x32
+
+ /* For devices with more than 2 channels, extra output volumes */
+#define AC97_LFE_MASTER_VOL 0x36
+#define AC97_SURR_MASTER_VOL 0x38
+
+ /* Digital SPDIF output control */
+#define AC97_SPDIF_OUT_CTRL 0x3a
+
+ /* Vendor ID identifier */
+#define AC97_VENDOR_ID1 0x7c
+#define AC97_VENDOR_ID2 0x7e
+
+/* EMP202 vendor registers */
+#define EM202_EXT_MODEM_CTRL 0x3e
+#define EM202_GPIO_CONF 0x4c
+#define EM202_GPIO_POLARITY 0x4e
+#define EM202_GPIO_STICKY 0x50
+#define EM202_GPIO_MASK 0x52
+#define EM202_GPIO_STATUS 0x54
+#define EM202_SPDIF_OUT_SEL 0x6a
+#define EM202_ANTIPOP 0x72
+#define EM202_EAPD_GPIO_ACCESS 0x74
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 4ea1f1e04897..53527536481e 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -39,6 +39,7 @@
#include "em28xx.h"
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
#include <media/msp3400.h>
#include <media/tuner.h>
@@ -47,9 +48,8 @@
"Mauro Carvalho Chehab <mchehab@infradead.org>, " \
"Sascha Sommer <saschasommer@freenet.de>"
-#define DRIVER_NAME "em28xx"
#define DRIVER_DESC "Empia em28xx based USB video device driver"
-#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 0)
+#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 1)
#define em28xx_videodbg(fmt, arg...) do {\
if (video_debug) \
@@ -72,19 +72,13 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-static LIST_HEAD(em28xx_devlist);
-static DEFINE_MUTEX(em28xx_devlist_mutex);
-
-static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
-module_param_array(card, int, NULL, 0444);
module_param_array(video_nr, int, NULL, 0444);
module_param_array(vbi_nr, int, NULL, 0444);
module_param_array(radio_nr, int, NULL, 0444);
-MODULE_PARM_DESC(card, "card type");
MODULE_PARM_DESC(video_nr, "video device numbers");
MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
MODULE_PARM_DESC(radio_nr, "radio device numbers");
@@ -93,8 +87,15 @@ static unsigned int video_debug;
module_param(video_debug, int, 0644);
MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
-/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
-static unsigned long em28xx_devused;
+/* supported video standards */
+static struct em28xx_fmt format[] = {
+ {
+ .name = "16bpp YUY2, 4:2:2, packed",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .reg = EM28XX_OUTFMT_YUV422_Y0UY1V,
+ },
+};
/* supported controls */
/* Common to all boards */
@@ -120,8 +121,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
}
};
-static struct usb_driver em28xx_usb_driver;
-
/* ------------------------------------------------------------------
DMA and thread functions
------------------------------------------------------------------*/
@@ -386,16 +385,18 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
struct em28xx *dev = fh->dev;
struct v4l2_frequency f;
- *size = 16 * fh->dev->width * fh->dev->height >> 3;
+ *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3;
+
if (0 == *count)
*count = EM28XX_DEF_BUF;
if (*count < EM28XX_MIN_BUF)
*count = EM28XX_MIN_BUF;
- /* Ask tuner to go to analog mode */
+ /* Ask tuner to go to analog or radio mode */
memset(&f, 0, sizeof(f));
f.frequency = dev->ctl_freq;
+ f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
@@ -438,9 +439,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
struct em28xx *dev = fh->dev;
int rc = 0, urb_init = 0;
- /* FIXME: It assumes depth = 16 */
- /* The only currently supported format is 16 bits/pixel */
- buf->vb.size = 16 * dev->width * dev->height >> 3;
+ buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
@@ -508,56 +507,6 @@ static struct videobuf_queue_ops em28xx_video_qops = {
/********************* v4l2 interface **************************************/
-/*
- * em28xx_config()
- * inits registers with sane defaults
- */
-static int em28xx_config(struct em28xx *dev)
-{
- int retval;
-
- /* Sets I2C speed to 100 KHz */
- if (!dev->is_em2800) {
- retval = em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1);
- if (retval < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n",
- __func__, retval);
- return retval;
- }
- }
-
- /* enable vbi capturing */
-
-/* em28xx_write_regs_req(dev, 0x00, 0x0e, "\xC0", 1); audio register */
-/* em28xx_write_regs_req(dev, 0x00, 0x0f, "\x80", 1); clk register */
- em28xx_write_regs_req(dev, 0x00, 0x11, "\x51", 1);
-
- dev->mute = 1; /* maybe not the right place... */
- dev->volume = 0x1f;
-
- em28xx_outfmt_set_yuv422(dev);
- em28xx_colorlevels_set_default(dev);
- em28xx_compression_disable(dev);
-
- return 0;
-}
-
-/*
- * em28xx_config_i2c()
- * configure i2c attached devices
- */
-static void em28xx_config_i2c(struct em28xx *dev)
-{
- struct v4l2_routing route;
- int zero = 0;
-
- route.input = INPUT(dev->ctl_input)->vmux;
- route.output = 0;
- em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero);
- em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
- em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
-}
-
static void video_mux(struct em28xx *dev, int index)
{
struct v4l2_routing route;
@@ -566,10 +515,14 @@ static void video_mux(struct em28xx *dev, int index)
route.output = 0;
dev->ctl_input = index;
dev->ctl_ainput = INPUT(index)->amux;
+ dev->ctl_aoutput = INPUT(index)->aout;
+
+ if (!dev->ctl_aoutput)
+ dev->ctl_aoutput = EM28XX_AOUT_MASTER;
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
- if (dev->has_msp34xx) {
+ if (dev->board.has_msp34xx) {
if (dev->i2s_speed) {
em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ,
&dev->i2s_speed);
@@ -595,12 +548,10 @@ static int res_get(struct em28xx_fh *fh)
return rc;
if (dev->stream_on)
- return -EINVAL;
+ return -EBUSY;
- mutex_lock(&dev->lock);
dev->stream_on = 1;
fh->stream_on = 1;
- mutex_unlock(&dev->lock);
return rc;
}
@@ -613,10 +564,8 @@ static void res_free(struct em28xx_fh *fh)
{
struct em28xx *dev = fh->dev;
- mutex_lock(&dev->lock);
fh->stream_on = 0;
dev->stream_on = 0;
- mutex_unlock(&dev->lock);
}
/*
@@ -703,8 +652,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.width = dev->width;
f->fmt.pix.height = dev->height;
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- f->fmt.pix.bytesperline = dev->width * 2;
+ f->fmt.pix.pixelformat = dev->format->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
@@ -716,6 +665,17 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
+static struct em28xx_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(format); i++)
+ if (format[i].fourcc == fourcc)
+ return &format[i];
+
+ return NULL;
+}
+
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
@@ -726,24 +686,30 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
unsigned int maxw = norm_maxw(dev);
unsigned int maxh = norm_maxh(dev);
unsigned int hscale, vscale;
+ struct em28xx_fmt *fmt;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ em28xx_videodbg("Fourcc format (%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
/* width must even because of the YUYV format
height must be even because of interlacing */
height &= 0xfffe;
- width &= 0xfffe;
+ width &= 0xfffe;
- if (height < 32)
+ if (unlikely(height < 32))
height = 32;
- if (height > maxh)
+ if (unlikely(height > maxh))
height = maxh;
- if (width < 48)
+ if (unlikely(width < 48))
width = 48;
- if (width > maxw)
+ if (unlikely(width > maxw))
width = maxw;
- mutex_lock(&dev->lock);
-
- if (dev->is_em2800) {
+ if (dev->board.is_em2800) {
/* the em2800 can only scale down to 50% */
if (height % (maxh / 2))
height = maxh;
@@ -766,13 +732,12 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.width = width;
f->fmt.pix.height = height;
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- f->fmt.pix.bytesperline = width * 2;
- f->fmt.pix.sizeimage = width * 2 * height;
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
- mutex_unlock(&dev->lock);
return 0;
}
@@ -782,14 +747,21 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
+ struct em28xx_fmt *fmt;
rc = check_dev(dev);
if (rc < 0)
return rc;
+ mutex_lock(&dev->lock);
+
vidioc_try_fmt_vid_cap(file, priv, f);
- mutex_lock(&dev->lock);
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ rc = -EINVAL;
+ goto out;
+ }
if (videobuf_queue_is_busy(&fh->vb_vidq)) {
em28xx_errdev("%s queue busy\n", __func__);
@@ -806,6 +778,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
/* set new image size */
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
+ dev->format = fmt;
get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
em28xx_set_alternate(dev);
@@ -831,15 +804,12 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
mutex_lock(&dev->lock);
dev->norm = *norm;
- mutex_unlock(&dev->lock);
/* Adjusts width/height, if needed */
f.fmt.pix.width = dev->width;
f.fmt.pix.height = dev->height;
vidioc_try_fmt_vid_cap(file, priv, &f);
- mutex_lock(&dev->lock);
-
/* set new image size */
dev->width = f.fmt.pix.width;
dev->height = f.fmt.pix.height;
@@ -928,20 +898,38 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
- unsigned int index = a->index;
-
- if (a->index > 1)
- return -EINVAL;
-
- index = dev->ctl_ainput;
- if (index == 0)
+ switch (a->index) {
+ case EM28XX_AMUX_VIDEO:
strcpy(a->name, "Television");
- else
+ break;
+ case EM28XX_AMUX_LINE_IN:
strcpy(a->name, "Line In");
+ break;
+ case EM28XX_AMUX_VIDEO2:
+ strcpy(a->name, "Television alt");
+ break;
+ case EM28XX_AMUX_PHONE:
+ strcpy(a->name, "Phone");
+ break;
+ case EM28XX_AMUX_MIC:
+ strcpy(a->name, "Mic");
+ break;
+ case EM28XX_AMUX_CD:
+ strcpy(a->name, "CD");
+ break;
+ case EM28XX_AMUX_AUX:
+ strcpy(a->name, "Aux");
+ break;
+ case EM28XX_AMUX_PCM_OUT:
+ strcpy(a->name, "PCM");
+ break;
+ default:
+ return -EINVAL;
+ }
+ a->index = dev->ctl_ainput;
a->capability = V4L2_AUDCAP_STEREO;
- a->index = index;
return 0;
}
@@ -951,9 +939,15 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
- if (a->index != dev->ctl_ainput)
- return -EINVAL;
+ mutex_lock(&dev->lock);
+
+ dev->ctl_ainput = INPUT(a->index)->amux;
+ dev->ctl_aoutput = INPUT(a->index)->aout;
+ if (!dev->ctl_aoutput)
+ dev->ctl_aoutput = EM28XX_AOUT_MASTER;
+
+ mutex_unlock(&dev->lock);
return 0;
}
@@ -974,7 +968,7 @@ static int vidioc_queryctrl(struct file *file, void *priv,
qc->id = id;
- if (!dev->has_msp34xx) {
+ if (!dev->board.has_msp34xx) {
for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
if (qc->id && qc->id == em28xx_qctrl[i].id) {
memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
@@ -1002,17 +996,14 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
rc = check_dev(dev);
if (rc < 0)
return rc;
- mutex_lock(&dev->lock);
+ rc = 0;
- if (!dev->has_msp34xx)
- rc = em28xx_get_ctrl(dev, ctrl);
- else
- rc = -EINVAL;
+ mutex_lock(&dev->lock);
- if (rc == -EINVAL) {
+ if (dev->board.has_msp34xx)
em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl);
- rc = 0;
- }
+ else
+ rc = em28xx_get_ctrl(dev, ctrl);
mutex_unlock(&dev->lock);
return rc;
@@ -1032,7 +1023,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
mutex_lock(&dev->lock);
- if (dev->has_msp34xx)
+ if (dev->board.has_msp34xx)
em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
else {
rc = 1;
@@ -1112,8 +1103,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
+ mutex_lock(&dev->lock);
f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
f->frequency = dev->ctl_freq;
+ mutex_unlock(&dev->lock);
return 0;
}
@@ -1143,6 +1136,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
mutex_unlock(&dev->lock);
+
return 0;
}
@@ -1159,6 +1153,21 @@ static int em28xx_reg_len(int reg)
}
}
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_chip_ident *chip)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+
+ em28xx_i2c_call_clients(dev, VIDIOC_G_CHIP_IDENT, chip);
+
+ return 0;
+}
+
+
static int vidioc_g_register(struct file *file, void *priv,
struct v4l2_register *reg)
{
@@ -1166,19 +1175,43 @@ static int vidioc_g_register(struct file *file, void *priv,
struct em28xx *dev = fh->dev;
int ret;
- if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ switch (reg->match_type) {
+ case V4L2_CHIP_MATCH_AC97:
+ mutex_lock(&dev->lock);
+ ret = em28xx_read_ac97(dev, reg->reg);
+ mutex_unlock(&dev->lock);
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_REGISTER, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* Not supported yet */
return -EINVAL;
+ default:
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+ }
+ /* Match host */
if (em28xx_reg_len(reg->reg) == 1) {
+ mutex_lock(&dev->lock);
ret = em28xx_read_reg(dev, reg->reg);
+ mutex_unlock(&dev->lock);
+
if (ret < 0)
return ret;
reg->val = ret;
} else {
__le64 val = 0;
+ mutex_lock(&dev->lock);
ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
reg->reg, (char *)&val, 2);
+ mutex_unlock(&dev->lock);
if (ret < 0)
return ret;
@@ -1194,11 +1227,35 @@ static int vidioc_s_register(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
__le64 buf;
+ int rc;
+ switch (reg->match_type) {
+ case V4L2_CHIP_MATCH_AC97:
+ mutex_lock(&dev->lock);
+ rc = em28xx_write_ac97(dev, reg->reg, reg->val);
+ mutex_unlock(&dev->lock);
+
+ return rc;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ em28xx_i2c_call_clients(dev, VIDIOC_DBG_S_REGISTER, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* Not supported yet */
+ return -EINVAL;
+ default:
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+ }
+
+ /* Match host */
buf = cpu_to_le64(reg->val);
- return em28xx_write_regs(dev, reg->reg, (char *)&buf,
- em28xx_reg_len(reg->reg));
+ mutex_lock(&dev->lock);
+ rc = em28xx_write_regs(dev, reg->reg, (char *)&buf,
+ em28xx_reg_len(reg->reg));
+ mutex_unlock(&dev->lock);
+
+ return rc;
}
#endif
@@ -1235,10 +1292,15 @@ static int vidioc_streamon(struct file *file, void *priv,
return rc;
- if (unlikely(res_get(fh) < 0))
- return -EBUSY;
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+
+ if (likely(rc >= 0))
+ rc = videobuf_streamon(&fh->vb_vidq);
- return (videobuf_streamon(&fh->vb_vidq));
+ mutex_unlock(&dev->lock);
+
+ return rc;
}
static int vidioc_streamoff(struct file *file, void *priv,
@@ -1257,9 +1319,13 @@ static int vidioc_streamoff(struct file *file, void *priv,
if (type != fh->type)
return -EINVAL;
+ mutex_lock(&dev->lock);
+
videobuf_streamoff(&fh->vb_vidq);
res_free(fh);
+ mutex_unlock(&dev->lock);
+
return 0;
}
@@ -1271,7 +1337,7 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
- strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
+ strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info));
cap->version = EM28XX_VERSION_CODE;
@@ -1288,15 +1354,13 @@ static int vidioc_querycap(struct file *file, void *priv,
}
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmtd)
+ struct v4l2_fmtdesc *f)
{
- if (fmtd->index != 0)
+ if (unlikely(f->index >= ARRAY_SIZE(format)))
return -EINVAL;
- fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strcpy(fmtd->description, "Packed YUY2");
- fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
- memset(fmtd->reserved, 0, sizeof(fmtd->reserved));
+ strlcpy(f->description, format[f->index].name, sizeof(f->description));
+ f->pixelformat = format[f->index].fourcc;
return 0;
}
@@ -1424,7 +1488,7 @@ static int radio_querycap(struct file *file, void *priv,
strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
- strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
+ strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info));
cap->version = EM28XX_VERSION_CODE;
cap->capabilities = V4L2_CAP_TUNER;
@@ -1442,7 +1506,10 @@ static int radio_g_tuner(struct file *file, void *priv,
strcpy(t->name, "Radio");
t->type = V4L2_TUNER_RADIO;
+ mutex_lock(&dev->lock);
em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+ mutex_unlock(&dev->lock);
+
return 0;
}
@@ -1474,7 +1541,9 @@ static int radio_s_tuner(struct file *file, void *priv,
if (0 != t->index)
return -EINVAL;
+ mutex_lock(&dev->lock);
em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
+ mutex_unlock(&dev->lock);
return 0;
}
@@ -1516,28 +1585,13 @@ static int radio_queryctrl(struct file *file, void *priv,
static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
{
int minor = iminor(inode);
- int errCode = 0, radio = 0;
- struct em28xx *h, *dev = NULL;
+ int errCode = 0, radio;
+ struct em28xx *dev;
+ enum v4l2_buf_type fh_type;
struct em28xx_fh *fh;
- enum v4l2_buf_type fh_type = 0;
- mutex_lock(&em28xx_devlist_mutex);
- list_for_each_entry(h, &em28xx_devlist, devlist) {
- if (h->vdev->minor == minor) {
- dev = h;
- fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- if (h->vbi_dev->minor == minor) {
- dev = h;
- fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
- }
- if (h->radio_dev &&
- h->radio_dev->minor == minor) {
- radio = 1;
- dev = h;
- }
- }
- mutex_unlock(&em28xx_devlist_mutex);
+ dev = em28xx_get_device(inode, &fh_type, &radio);
+
if (NULL == dev)
return -ENODEV;
@@ -1571,7 +1625,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
/* Needed, since GPIO might have disabled power of
some i2c device
*/
- em28xx_config_i2c(dev);
+ em28xx_wake_i2c(dev);
}
if (fh->radio) {
@@ -1595,16 +1649,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
* unregisters the v4l2,i2c and usb devices
* called when the device gets disconected or at module unload
*/
-static void em28xx_release_resources(struct em28xx *dev)
+void em28xx_release_analog_resources(struct em28xx *dev)
{
/*FIXME: I2C IR should be disconnected */
- em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
- dev->vdev->num, dev->vbi_dev->num);
- list_del(&dev->devlist);
- if (dev->sbutton_input_dev)
- em28xx_deregister_snapshot_button(dev);
if (dev->radio_dev) {
if (-1 != dev->radio_dev->minor)
video_unregister_device(dev->radio_dev);
@@ -1613,6 +1662,8 @@ static void em28xx_release_resources(struct em28xx *dev)
dev->radio_dev = NULL;
}
if (dev->vbi_dev) {
+ em28xx_info("V4L2 device /dev/vbi%d deregistered\n",
+ dev->vbi_dev->num);
if (-1 != dev->vbi_dev->minor)
video_unregister_device(dev->vbi_dev);
else
@@ -1620,17 +1671,14 @@ static void em28xx_release_resources(struct em28xx *dev)
dev->vbi_dev = NULL;
}
if (dev->vdev) {
+ em28xx_info("V4L2 device /dev/video%d deregistered\n",
+ dev->vdev->num);
if (-1 != dev->vdev->minor)
video_unregister_device(dev->vdev);
else
video_device_release(dev->vdev);
dev->vdev = NULL;
}
- em28xx_i2c_unregister(dev);
- usb_put_dev(dev->udev);
-
- /* Mark device as unused */
- em28xx_devused &= ~(1<<dev->devno);
}
/*
@@ -1647,11 +1695,10 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
em28xx_videodbg("users=%d\n", dev->users);
+ mutex_lock(&dev->lock);
if (res_check(fh))
res_free(fh);
- mutex_lock(&dev->lock);
-
if (dev->users == 1) {
videobuf_stop(&fh->vb_vidq);
videobuf_mmap_free(&fh->vb_vidq);
@@ -1665,9 +1712,12 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
return 0;
}
+ /* Save some power by putting tuner to sleep */
+ em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
+
/* do this before setting alternate! */
em28xx_uninit_isoc(dev);
- em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
/* set alternate 0 */
dev->alt = 0;
@@ -1706,8 +1756,12 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
*/
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (unlikely(res_get(fh)))
- return -EBUSY;
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+ mutex_unlock(&dev->lock);
+
+ if (unlikely(rc < 0))
+ return rc;
return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
filp->f_flags & O_NONBLOCK);
@@ -1729,7 +1783,11 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
if (rc < 0)
return rc;
- if (unlikely(res_get(fh) < 0))
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+ mutex_unlock(&dev->lock);
+
+ if (unlikely(rc < 0))
return POLLERR;
if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
@@ -1747,13 +1805,17 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
struct em28xx *dev = fh->dev;
int rc;
- if (unlikely(res_get(fh) < 0))
- return -EBUSY;
-
rc = check_dev(dev);
if (rc < 0)
return rc;
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+ mutex_unlock(&dev->lock);
+
+ if (unlikely(rc < 0))
+ return rc;
+
rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
@@ -1810,6 +1872,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
#endif
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
@@ -1865,44 +1928,6 @@ static struct video_device em28xx_radio_template = {
/******************************** usb interface ******************************/
-static LIST_HEAD(em28xx_extension_devlist);
-static DEFINE_MUTEX(em28xx_extension_devlist_lock);
-
-int em28xx_register_extension(struct em28xx_ops *ops)
-{
- struct em28xx *dev = NULL;
-
- mutex_lock(&em28xx_devlist_mutex);
- mutex_lock(&em28xx_extension_devlist_lock);
- list_add_tail(&ops->next, &em28xx_extension_devlist);
- list_for_each_entry(dev, &em28xx_devlist, devlist) {
- if (dev)
- ops->init(dev);
- }
- printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
- mutex_unlock(&em28xx_extension_devlist_lock);
- mutex_unlock(&em28xx_devlist_mutex);
- return 0;
-}
-EXPORT_SYMBOL(em28xx_register_extension);
-
-void em28xx_unregister_extension(struct em28xx_ops *ops)
-{
- struct em28xx *dev = NULL;
-
- mutex_lock(&em28xx_devlist_mutex);
- list_for_each_entry(dev, &em28xx_devlist, devlist) {
- if (dev)
- ops->fini(dev);
- }
-
- mutex_lock(&em28xx_extension_devlist_lock);
- printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
- list_del(&ops->next);
- mutex_unlock(&em28xx_extension_devlist_lock);
- mutex_unlock(&em28xx_devlist_mutex);
-}
-EXPORT_SYMBOL(em28xx_unregister_extension);
static struct video_device *em28xx_vdev_init(struct em28xx *dev,
const struct video_device *template,
@@ -1925,11 +1950,43 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
return vfd;
}
-
-static int register_analog_devices(struct em28xx *dev)
+int em28xx_register_analog_devices(struct em28xx *dev)
{
int ret;
+ printk(KERN_INFO "%s: v4l2 driver version %d.%d.%d\n",
+ dev->name,
+ (EM28XX_VERSION_CODE >> 16) & 0xff,
+ (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);
+
+ /* Analog specific initialization */
+ dev->format = &format[0];
+ video_mux(dev, 0);
+
+ /* enable vbi capturing */
+
+/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
+/* em28xx_write_reg(dev, EM28XX_R0F_XCLK, 0x80); clk register */
+ em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51);
+
+ dev->mute = 1; /* maybe not the right place... */
+ dev->volume = 0x1f;
+
+ em28xx_set_outfmt(dev);
+ em28xx_colorlevels_set_default(dev);
+ em28xx_compression_disable(dev);
+
+ /* set default norm */
+ dev->norm = em28xx_video_template.current_norm;
+ dev->width = norm_maxw(dev);
+ dev->height = norm_maxh(dev);
+ dev->interlaced = EM28XX_INTERLACED_DEFAULT;
+ dev->hscale = 0;
+ dev->vscale = 0;
+
+ /* FIXME: This is a very bad hack! Not all devices have TV on input 2 */
+ dev->ctl_input = 2;
+
/* allocate and fill video video_device struct */
dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video");
if (!dev->vdev) {
@@ -1978,383 +2035,3 @@ static int register_analog_devices(struct em28xx *dev)
return 0;
}
-
-
-/*
- * em28xx_init_dev()
- * allocates and inits the device structs, registers i2c bus and v4l device
- */
-static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
- int minor)
-{
- struct em28xx_ops *ops = NULL;
- struct em28xx *dev = *devhandle;
- int retval = -ENOMEM;
- int errCode;
- unsigned int maxh, maxw;
-
- dev->udev = udev;
- mutex_init(&dev->lock);
- mutex_init(&dev->ctrl_urb_lock);
- spin_lock_init(&dev->slock);
- init_waitqueue_head(&dev->open);
- init_waitqueue_head(&dev->wait_frame);
- init_waitqueue_head(&dev->wait_stream);
-
- dev->em28xx_write_regs = em28xx_write_regs;
- dev->em28xx_read_reg = em28xx_read_reg;
- dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
- dev->em28xx_write_regs_req = em28xx_write_regs_req;
- dev->em28xx_read_reg_req = em28xx_read_reg_req;
- dev->is_em2800 = em28xx_boards[dev->model].is_em2800;
-
- em28xx_pre_card_setup(dev);
-
- errCode = em28xx_config(dev);
- if (errCode) {
- em28xx_errdev("error configuring device\n");
- return -ENOMEM;
- }
-
- /* register i2c bus */
- errCode = em28xx_i2c_register(dev);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n",
- __func__, errCode);
- return errCode;
- }
-
- /* Do board specific init and eeprom reading */
- em28xx_card_setup(dev);
-
- /* Configure audio */
- errCode = em28xx_audio_analog_set(dev);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_audio_analog_set - errCode [%d]!\n",
- __func__, errCode);
- return errCode;
- }
-
- /* configure the device */
- em28xx_config_i2c(dev);
-
- /* set default norm */
- dev->norm = em28xx_video_template.current_norm;
-
- maxw = norm_maxw(dev);
- maxh = norm_maxh(dev);
-
- /* set default image size */
- dev->width = maxw;
- dev->height = maxh;
- dev->interlaced = EM28XX_INTERLACED_DEFAULT;
- dev->hscale = 0;
- dev->vscale = 0;
- dev->ctl_input = 2;
-
- errCode = em28xx_config(dev);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_config - errCode [%d]!\n",
- __func__, errCode);
- return errCode;
- }
-
- /* init video dma queues */
- INIT_LIST_HEAD(&dev->vidq.active);
- INIT_LIST_HEAD(&dev->vidq.queued);
-
-
- if (dev->has_msp34xx) {
- /* Send a reset to other chips via gpio */
- errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(1) failed! errCode [%d]\n",
- __func__, errCode);
- return errCode;
- }
- msleep(3);
-
- errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(2) failed! errCode [%d]\n",
- __func__, errCode);
- return errCode;
- }
- msleep(3);
- }
-
- video_mux(dev, 0);
-
- mutex_lock(&em28xx_devlist_mutex);
- list_add_tail(&dev->devlist, &em28xx_devlist);
- retval = register_analog_devices(dev);
- if (retval < 0) {
- em28xx_release_resources(dev);
- mutex_unlock(&em28xx_devlist_mutex);
- goto fail_reg_devices;
- }
-
- mutex_lock(&em28xx_extension_devlist_lock);
- if (!list_empty(&em28xx_extension_devlist)) {
- list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->id)
- ops->init(dev);
- }
- }
- mutex_unlock(&em28xx_extension_devlist_lock);
- mutex_unlock(&em28xx_devlist_mutex);
-
- return 0;
-
-fail_reg_devices:
- mutex_unlock(&dev->lock);
- return retval;
-}
-
-#if defined(CONFIG_MODULES) && defined(MODULE)
-static void request_module_async(struct work_struct *work)
-{
- struct em28xx *dev = container_of(work,
- struct em28xx, request_module_wk);
-
- if (dev->has_audio_class)
- request_module("snd-usb-audio");
- else
- request_module("em28xx-alsa");
-
- if (dev->has_dvb)
- request_module("em28xx-dvb");
-}
-
-static void request_modules(struct em28xx *dev)
-{
- INIT_WORK(&dev->request_module_wk, request_module_async);
- schedule_work(&dev->request_module_wk);
-}
-#else
-#define request_modules(dev)
-#endif /* CONFIG_MODULES */
-
-/*
- * em28xx_usb_probe()
- * checks for supported devices
- */
-static int em28xx_usb_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- const struct usb_endpoint_descriptor *endpoint;
- struct usb_device *udev;
- struct usb_interface *uif;
- struct em28xx *dev = NULL;
- int retval = -ENODEV;
- int i, nr, ifnum;
-
- udev = usb_get_dev(interface_to_usbdev(interface));
- ifnum = interface->altsetting[0].desc.bInterfaceNumber;
-
- /* Check to see next free device and mark as used */
- nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
- em28xx_devused |= 1<<nr;
-
- /* Don't register audio interfaces */
- if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
- em28xx_err(DRIVER_NAME " audio device (%04x:%04x): interface %i, class %i\n",
- udev->descriptor.idVendor,
- udev->descriptor.idProduct,
- ifnum,
- interface->altsetting[0].desc.bInterfaceClass);
-
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
- }
-
- em28xx_err(DRIVER_NAME " new video device (%04x:%04x): interface %i, class %i\n",
- udev->descriptor.idVendor,
- udev->descriptor.idProduct,
- ifnum,
- interface->altsetting[0].desc.bInterfaceClass);
-
- endpoint = &interface->cur_altsetting->endpoint[1].desc;
-
- /* check if the device has the iso in endpoint at the correct place */
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
- USB_ENDPOINT_XFER_ISOC) {
- em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n");
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
- }
- if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
- em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n");
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
- }
-
- if (nr >= EM28XX_MAXBOARDS) {
- printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
- EM28XX_MAXBOARDS);
- em28xx_devused &= ~(1<<nr);
- return -ENOMEM;
- }
-
- /* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- em28xx_err(DRIVER_NAME ": out of memory!\n");
- em28xx_devused &= ~(1<<nr);
- return -ENOMEM;
- }
-
- snprintf(dev->name, 29, "em28xx #%d", nr);
- dev->devno = nr;
- dev->model = id->driver_info;
- dev->alt = -1;
-
- /* Checks if audio is provided by some interface */
- for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
- uif = udev->config->interface[i];
- if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
- dev->has_audio_class = 1;
- break;
- }
- }
-
- printk(KERN_INFO DRIVER_NAME " %s usb audio class\n",
- dev->has_audio_class ? "Has" : "Doesn't have");
-
- /* compute alternate max packet sizes */
- uif = udev->actconfig->interface[0];
-
- dev->num_alt = uif->num_altsetting;
- em28xx_info("Alternate settings: %i\n", dev->num_alt);
-/* dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* */
- dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL);
-
- if (dev->alt_max_pkt_size == NULL) {
- em28xx_errdev("out of memory!\n");
- em28xx_devused &= ~(1<<nr);
- kfree(dev);
- return -ENOMEM;
- }
-
- for (i = 0; i < dev->num_alt ; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc.
- wMaxPacketSize);
- dev->alt_max_pkt_size[i] =
- (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
- em28xx_info("Alternate setting %i, max size= %i\n", i,
- dev->alt_max_pkt_size[i]);
- }
-
- if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
- dev->model = card[nr];
-
- /* allocate device struct */
- retval = em28xx_init_dev(&dev, udev, nr);
- if (retval) {
- em28xx_devused &= ~(1<<dev->devno);
- kfree(dev);
-
- return retval;
- }
-
- em28xx_info("Found %s\n", em28xx_boards[dev->model].name);
-
- /* save our data pointer in this interface device */
- usb_set_intfdata(interface, dev);
-
- request_modules(dev);
-
- return 0;
-}
-
-/*
- * em28xx_usb_disconnect()
- * called when the device gets diconencted
- * video device will be unregistered on v4l2_close in case it is still open
- */
-static void em28xx_usb_disconnect(struct usb_interface *interface)
-{
- struct em28xx *dev;
- struct em28xx_ops *ops = NULL;
-
- dev = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
-
- if (!dev)
- return;
-
- em28xx_info("disconnecting %s\n", dev->vdev->name);
-
- /* wait until all current v4l2 io is finished then deallocate
- resources */
- mutex_lock(&dev->lock);
-
- wake_up_interruptible_all(&dev->open);
-
- if (dev->users) {
- em28xx_warn
- ("device /dev/video%d is open! Deregistration and memory "
- "deallocation are deferred on close.\n",
- dev->vdev->num);
-
- dev->state |= DEV_MISCONFIGURED;
- em28xx_uninit_isoc(dev);
- dev->state |= DEV_DISCONNECTED;
- wake_up_interruptible(&dev->wait_frame);
- wake_up_interruptible(&dev->wait_stream);
- } else {
- dev->state |= DEV_DISCONNECTED;
- em28xx_release_resources(dev);
- }
- mutex_unlock(&dev->lock);
-
- mutex_lock(&em28xx_extension_devlist_lock);
- if (!list_empty(&em28xx_extension_devlist)) {
- list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- ops->fini(dev);
- }
- }
- mutex_unlock(&em28xx_extension_devlist_lock);
-
- if (!dev->users) {
- kfree(dev->alt_max_pkt_size);
- kfree(dev);
- }
-}
-
-static struct usb_driver em28xx_usb_driver = {
- .name = "em28xx",
- .probe = em28xx_usb_probe,
- .disconnect = em28xx_usb_disconnect,
- .id_table = em28xx_id_table,
-};
-
-static int __init em28xx_module_init(void)
-{
- int result;
-
- printk(KERN_INFO DRIVER_NAME " v4l2 driver version %d.%d.%d loaded\n",
- (EM28XX_VERSION_CODE >> 16) & 0xff,
- (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
- printk(KERN_INFO DRIVER_NAME " snapshot date %04d-%02d-%02d\n",
- SNAPSHOT / 10000, (SNAPSHOT / 100) % 100, SNAPSHOT % 100);
-#endif
-
- /* register this driver with the USB subsystem */
- result = usb_register(&em28xx_usb_driver);
- if (result)
- em28xx_err(DRIVER_NAME
- " usb_register failed. Error number %d.\n", result);
-
- return result;
-}
-
-static void __exit em28xx_module_exit(void)
-{
- /* deregister this driver with the USB subsystem */
- usb_deregister(&em28xx_usb_driver);
-}
-
-module_init(em28xx_module_init);
-module_exit(em28xx_module_exit);
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 5956e9b3062f..b5eddc26388e 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -67,7 +67,6 @@
#define EM2820_BOARD_HERCULES_SMART_TV_USB2 26
#define EM2820_BOARD_PINNACLE_USB_2_FM1216ME 27
#define EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE 28
-#define EM2820_BOARD_PINNACLE_DVC_100 29
#define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30
#define EM2821_BOARD_USBGEAR_VD204 31
#define EM2821_BOARD_SUPERCOMP_USB_2 32
@@ -97,6 +96,8 @@
#define EM2882_BOARD_PINNACLE_HYBRID_PRO 56
#define EM2883_BOARD_KWORLD_HYBRID_A316 57
#define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58
+#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60
+#define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -159,7 +160,7 @@
#define EM2800_I2C_WRITE_TIMEOUT 20
enum em28xx_mode {
- EM28XX_MODE_UNDEFINED,
+ EM28XX_SUSPEND,
EM28XX_ANALOG_MODE,
EM28XX_DIGITAL_MODE,
};
@@ -207,9 +208,12 @@ struct em28xx_usb_isoc_ctl {
};
+/* Struct to enumberate video formats */
struct em28xx_fmt {
char *name;
u32 fourcc; /* v4l2 format id */
+ int depth;
+ int reg;
};
/* buffer for one video frame */
@@ -255,54 +259,105 @@ enum enum28xx_itype {
EM28XX_RADIO,
};
+enum em28xx_ac97_mode {
+ EM28XX_NO_AC97 = 0,
+ EM28XX_AC97_EM202,
+ EM28XX_AC97_SIGMATEL,
+ EM28XX_AC97_OTHER,
+};
+
+struct em28xx_audio_mode {
+ enum em28xx_ac97_mode ac97;
+
+ u16 ac97_feat;
+ u32 ac97_vendor_id;
+
+ unsigned int has_audio:1;
+
+ unsigned int i2s_3rates:1;
+ unsigned int i2s_5rates:1;
+};
+
+/* em28xx has two audio inputs: tuner and line in.
+ However, on most devices, an auxiliary AC97 codec device is used.
+ The AC97 device may have several different inputs and outputs,
+ depending on their model. So, it is possible to use AC97 mixer to
+ address more than two different entries.
+ */
enum em28xx_amux {
- EM28XX_AMUX_VIDEO,
- EM28XX_AMUX_LINE_IN,
- EM28XX_AMUX_AC97_VIDEO,
- EM28XX_AMUX_AC97_LINE_IN,
+ /* This is the only entry for em28xx tuner input */
+ EM28XX_AMUX_VIDEO, /* em28xx tuner, AC97 mixer Video */
+
+ EM28XX_AMUX_LINE_IN, /* AC97 mixer Line In */
+
+ /* Some less-common mixer setups */
+ EM28XX_AMUX_VIDEO2, /* em28xx Line in, AC97 mixer Video */
+ EM28XX_AMUX_PHONE,
+ EM28XX_AMUX_MIC,
+ EM28XX_AMUX_CD,
+ EM28XX_AMUX_AUX,
+ EM28XX_AMUX_PCM_OUT,
+};
+
+enum em28xx_aout {
+ EM28XX_AOUT_MASTER = 1 << 0,
+ EM28XX_AOUT_LINE = 1 << 1,
+ EM28XX_AOUT_MONO = 1 << 2,
+ EM28XX_AOUT_LFE = 1 << 3,
+ EM28XX_AOUT_SURR = 1 << 4,
+};
+
+struct em28xx_reg_seq {
+ int reg;
+ unsigned char val, mask;
+ int sleep;
};
struct em28xx_input {
enum enum28xx_itype type;
unsigned int vmux;
enum em28xx_amux amux;
+ enum em28xx_aout aout;
+ struct em28xx_reg_seq *gpio;
};
#define INPUT(nr) (&em28xx_boards[dev->model].input[nr])
enum em28xx_decoder {
+ EM28XX_NODECODER,
EM28XX_TVP5150,
- EM28XX_SAA7113,
- EM28XX_SAA7114
-};
-
-struct em28xx_reg_seq {
- int reg;
- unsigned char val, mask;
- int sleep;
+ EM28XX_SAA711X,
};
struct em28xx_board {
char *name;
int vchannels;
int tuner_type;
+ int tuner_addr;
/* i2c flags */
unsigned int tda9887_conf;
+ /* GPIO sequences */
+ struct em28xx_reg_seq *dvb_gpio;
+ struct em28xx_reg_seq *suspend_gpio;
+ struct em28xx_reg_seq *tuner_gpio;
+
unsigned int is_em2800:1;
unsigned int has_msp34xx:1;
unsigned int mts_firmware:1;
- unsigned int has_12mhz_i2s:1;
unsigned int max_range_640_480:1;
unsigned int has_dvb:1;
unsigned int has_snapshot_button:1;
unsigned int valid:1;
+ unsigned char xclk, i2c_speed;
+
enum em28xx_decoder decoder;
struct em28xx_input input[MAX_EM28XX_INPUT];
struct em28xx_input radio;
+ IR_KEYTAB_TYPE *ir_codes;
};
struct em28xx_eeprom {
@@ -369,32 +424,26 @@ struct em28xx {
char name[30]; /* name (including minor) of the device */
int model; /* index in the device_data struct */
int devno; /* marks the number of this device */
- unsigned int is_em2800:1;
- unsigned int has_msp34xx:1;
- unsigned int has_tda9887:1;
+ enum em28xx_chip_id chip_id;
+
+ struct em28xx_board board;
+
unsigned int stream_on:1; /* Locks streams */
unsigned int has_audio_class:1;
- unsigned int has_12mhz_i2s:1;
- unsigned int max_range_640_480:1;
- unsigned int has_dvb:1;
- unsigned int has_snapshot_button:1;
- unsigned int valid:1; /* report for validated boards */
+ unsigned int has_alsa_audio:1;
- /* Some older em28xx chips needs a waiting time after writing */
- unsigned int wait_after_write;
+ struct em28xx_fmt *format;
- /* GPIO sequences for analog and digital mode */
- struct em28xx_reg_seq *analog_gpio, *digital_gpio;
+ struct em28xx_IR *ir;
- /* GPIO sequences for tuner callbacks */
- struct em28xx_reg_seq *tun_analog_gpio, *tun_digital_gpio;
+ /* Some older em28xx chips needs a waiting time after writing */
+ unsigned int wait_after_write;
- int video_inputs; /* number of video inputs */
struct list_head devlist;
u32 i2s_speed; /* I2S speed for audio digital stream */
- enum em28xx_decoder decoder;
+ struct em28xx_audio_mode audio_mode;
int tuner_type; /* type of the tuner */
int tuner_addr; /* tuner address */
@@ -409,6 +458,7 @@ struct em28xx {
int ctl_freq; /* selected frequency */
unsigned int ctl_input; /* selected input */
unsigned int ctl_ainput;/* selected audio input */
+ unsigned int ctl_aoutput;/* selected audio output */
int mute;
int volume;
/* frame properties */
@@ -469,6 +519,9 @@ struct em28xx {
enum em28xx_mode mode;
+ /* register numbers for GPO/GPIO registers */
+ u16 reg_gpo_num, reg_gpio_num;
+
/* Caches GPO and GPIO registers */
unsigned char reg_gpo, reg_gpio;
@@ -508,11 +561,17 @@ int em28xx_read_reg(struct em28xx *dev, u16 reg);
int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
int len);
int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len);
+int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val);
+
+int em28xx_read_ac97(struct em28xx *dev, u8 reg);
+int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val);
+
int em28xx_audio_analog_set(struct em28xx *dev);
+int em28xx_audio_setup(struct em28xx *dev);
int em28xx_colorlevels_set_default(struct em28xx *dev);
int em28xx_capture_start(struct em28xx *dev, int start);
-int em28xx_outfmt_set_yuv422(struct em28xx *dev);
+int em28xx_set_outfmt(struct em28xx *dev);
int em28xx_resolution_set(struct em28xx *dev);
int em28xx_set_alternate(struct em28xx *dev);
int em28xx_init_isoc(struct em28xx *dev, int max_packets,
@@ -521,10 +580,20 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
void em28xx_uninit_isoc(struct em28xx *dev);
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
-
-/* Provided by em28xx-video.c */
+void em28xx_wake_i2c(struct em28xx *dev);
+void em28xx_remove_from_devlist(struct em28xx *dev);
+void em28xx_add_into_devlist(struct em28xx *dev);
+struct em28xx *em28xx_get_device(struct inode *inode,
+ enum v4l2_buf_type *fh_type,
+ int *has_radio);
int em28xx_register_extension(struct em28xx_ops *dev);
void em28xx_unregister_extension(struct em28xx_ops *dev);
+void em28xx_init_extension(struct em28xx *dev);
+void em28xx_close_extension(struct em28xx *dev);
+
+/* Provided by em28xx-video.c */
+int em28xx_register_analog_devices(struct em28xx *dev);
+void em28xx_release_analog_resources(struct em28xx *dev);
/* Provided by em28xx-cards.c */
extern int em2800_variant_detect(struct usb_device *udev, int model);
@@ -535,9 +604,9 @@ extern struct usb_device_id em28xx_id_table[];
extern const unsigned int em28xx_bcount;
void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir);
int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
+void em28xx_release_resources(struct em28xx *dev);
/* Provided by em28xx-input.c */
-/* TODO: Check if the standard get_key handlers on ir-common can be used */
int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
@@ -545,6 +614,9 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
void em28xx_register_snapshot_button(struct em28xx *dev);
void em28xx_deregister_snapshot_button(struct em28xx *dev);
+int em28xx_ir_init(struct em28xx *dev);
+int em28xx_ir_fini(struct em28xx *dev);
+
/* printk macros */
#define em28xx_err(fmt, arg...) do {\
@@ -564,7 +636,7 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev);
static inline int em28xx_compression_disable(struct em28xx *dev)
{
/* side effect of disabling scaler and mixer */
- return em28xx_write_regs(dev, EM28XX_R26_COMPR, "\x00", 1);
+ return em28xx_write_reg(dev, EM28XX_R26_COMPR, 0x00);
}
static inline int em28xx_contrast_get(struct em28xx *dev)
@@ -636,7 +708,7 @@ static inline int em28xx_gamma_set(struct em28xx *dev, s32 val)
/*FIXME: maxw should be dependent of alt mode */
static inline unsigned int norm_maxw(struct em28xx *dev)
{
- if (dev->max_range_640_480)
+ if (dev->board.max_range_640_480)
return 640;
else
return 720;
@@ -644,7 +716,7 @@ static inline unsigned int norm_maxw(struct em28xx *dev)
static inline unsigned int norm_maxh(struct em28xx *dev)
{
- if (dev->max_range_640_480)
+ if (dev->board.max_range_640_480)
return 480;
else
return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c
index 9d0ef96c23ff..83c07112c59d 100644
--- a/drivers/media/video/et61x251/et61x251_core.c
+++ b/drivers/media/video/et61x251/et61x251_core.c
@@ -1581,7 +1581,7 @@ et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg)
strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
- strlcpy(cap.bus_info, cam->usbdev->dev.bus_id,
+ strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev),
sizeof(cap.bus_info));
if (copy_to_user(arg, &cap, sizeof(cap)))
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
index 6b557c057fac..ee6a691dff22 100644
--- a/drivers/media/video/gspca/Kconfig
+++ b/drivers/media/video/gspca/Kconfig
@@ -12,12 +12,13 @@ menuconfig USB_GSPCA
"Video For Linux" to use this driver.
To compile this driver as modules, choose M here: the
- modules will be called gspca_main.
+ module will be called gspca_main.
if USB_GSPCA && VIDEO_V4L2
source "drivers/media/video/gspca/m5602/Kconfig"
+source "drivers/media/video/gspca/stv06xx/Kconfig"
config USB_GSPCA_CONEX
tristate "Conexant Camera Driver"
@@ -64,6 +65,16 @@ config USB_GSPCA_OV519
To compile this driver as a module, choose M here: the
module will be called gspca_ov519.
+config USB_GSPCA_OV534
+ tristate "OV534 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the OV534 chip.
+ (e.g. Sony Playstation EYE)
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov534.
+
config USB_GSPCA_PAC207
tristate "Pixart PAC207 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
@@ -83,10 +94,11 @@ config USB_GSPCA_PAC7311
module will be called gspca_pac7311.
config USB_GSPCA_SONIXB
- tristate "SN9C102 USB Camera Driver"
+ tristate "SONIX Bayer USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
- Say Y here if you want support for cameras based on the SONIXB chip.
+ Say Y here if you want support for cameras based on the Sonix
+ chips with Bayer format (SN9C101, SN9C102 and SN9C103).
To compile this driver as a module, choose M here: the
module will be called gspca_sonixb.
@@ -95,7 +107,8 @@ config USB_GSPCA_SONIXJ
tristate "SONIX JPEG USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
- Say Y here if you want support for cameras based on the SONIXJ chip.
+ Say Y here if you want support for cameras based on the Sonix
+ chips with JPEG format (SN9C102P, SN9C105 and >= SN9C110).
To compile this driver as a module, choose M here: the
module will be called gspca_sonixj
@@ -171,7 +184,7 @@ config USB_GSPCA_SUNPLUS
SPCA504(abc) SPCA533 SPCA536 chips.
To compile this driver as a module, choose M here: the
- module will be called gspca_spca5xx.
+ module will be called gspca_sunplus.
config USB_GSPCA_T613
tristate "T613 (JPEG Compliance) USB Camera Driver"
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
index 22734f5a6c32..bd8d9ee40504 100644
--- a/drivers/media/video/gspca/Makefile
+++ b/drivers/media/video/gspca/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o
obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o
obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o
obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o
+obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o
obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o
obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o
obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o
@@ -27,6 +28,7 @@ gspca_etoms-objs := etoms.o
gspca_finepix-objs := finepix.o
gspca_mars-objs := mars.o
gspca_ov519-objs := ov519.o
+gspca_ov534-objs := ov534.o
gspca_pac207-objs := pac207.o
gspca_pac7311-objs := pac7311.o
gspca_sonixb-objs := sonixb.o
@@ -45,4 +47,4 @@ gspca_vc032x-objs := vc032x.o
gspca_zc3xx-objs := zc3xx.o
obj-$(CONFIG_USB_M5602) += m5602/
-
+obj-$(CONFIG_USB_STV06XX) += stv06xx/
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
index de28354ea5ba..1753f5bb3544 100644
--- a/drivers/media/video/gspca/conex.c
+++ b/drivers/media/video/gspca/conex.c
@@ -93,7 +93,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
index 3be30b420a26..f3cd8ff5cc92 100644
--- a/drivers/media/video/gspca/etoms.c
+++ b/drivers/media/video/gspca/etoms.c
@@ -112,7 +112,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240,
@@ -125,7 +125,7 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 0}, */
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144,
diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
index 607942fd7970..afc8b2dd307b 100644
--- a/drivers/media/video/gspca/finepix.c
+++ b/drivers/media/video/gspca/finepix.c
@@ -72,7 +72,7 @@ struct usb_fpix {
}
/* These cameras only support 320x200. */
-static struct v4l2_pix_format fpix_mode[1] = {
+static const struct v4l2_pix_format fpix_mode[1] = {
{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -314,9 +314,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
int ret;
int size_ret;
- /* Reset bulk in endpoint */
- usb_clear_halt(gspca_dev->dev, gspca_dev->cam.epaddr);
-
/* Init the device */
memset(gspca_dev->usb_buf, 0, 12);
gspca_dev->usb_buf[0] = 0xc6;
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index 02a6e9ef0337..8b9f3bde5740 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -30,7 +30,6 @@
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/io.h>
-#include <linux/kref.h>
#include <asm/page.h>
#include <linux/uaccess.h>
#include <linux/jiffies.h>
@@ -45,7 +44,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("GSPCA USB Camera Driver");
MODULE_LICENSE("GPL");
-#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 3, 0)
+#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 4, 0)
static int video_nr = -1;
@@ -150,8 +149,11 @@ static void fill_frame(struct gspca_dev *gspca_dev,
/* check the packet status and length */
len = urb->iso_frame_desc[i].actual_length;
- if (len == 0)
+ if (len == 0) {
+ if (gspca_dev->empty_packet == 0)
+ gspca_dev->empty_packet = 1;
continue;
+ }
st = urb->iso_frame_desc[i].status;
if (st) {
PDEBUG(D_ERR,
@@ -170,7 +172,6 @@ static void fill_frame(struct gspca_dev *gspca_dev,
}
/* resubmit the URB */
- urb->status = 0;
st = usb_submit_urb(urb, GFP_ATOMIC);
if (st < 0)
PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);
@@ -200,11 +201,18 @@ static void bulk_irq(struct urb *urb
{
struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
struct gspca_frame *frame;
+ int st;
PDEBUG(D_PACK, "bulk irq");
if (!gspca_dev->streaming)
return;
- if (urb->status != 0 && urb->status != -ECONNRESET) {
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET:
+ urb->status = 0;
+ break;
+ default:
#ifdef CONFIG_PM
if (!gspca_dev->frozen)
#endif
@@ -223,6 +231,13 @@ static void bulk_irq(struct urb *urb
urb->transfer_buffer,
urb->actual_length);
}
+
+ /* resubmit the URB */
+ if (gspca_dev->cam.bulk_nurbs != 0) {
+ st = usb_submit_urb(urb, GFP_ATOMIC);
+ if (st < 0)
+ PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);
+ }
}
/*
@@ -285,7 +300,6 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
frame->v4l2_buf.bytesused = frame->data_end - frame->data;
frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED;
frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE;
- atomic_inc(&gspca_dev->nevent);
wake_up_interruptible(&gspca_dev->wq); /* event = new frame */
i = (gspca_dev->fr_i + 1) % gspca_dev->nframes;
gspca_dev->fr_i = i;
@@ -379,7 +393,6 @@ static int frame_alloc(struct gspca_dev *gspca_dev,
gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
gspca_dev->last_packet_type = DISCARD_PACKET;
gspca_dev->sequence = 0;
- atomic_set(&gspca_dev->nevent, 0);
return 0;
}
@@ -520,11 +533,14 @@ static int create_urbs(struct gspca_dev *gspca_dev,
nurbs = DEF_NURBS;
} else { /* bulk */
npkt = 0;
- bsize = gspca_dev->cam. bulk_size;
+ bsize = gspca_dev->cam.bulk_size;
if (bsize == 0)
bsize = psize;
PDEBUG(D_STREAM, "bulk bsize:%d", bsize);
- nurbs = 1;
+ if (gspca_dev->cam.bulk_nurbs != 0)
+ nurbs = gspca_dev->cam.bulk_nurbs;
+ else
+ nurbs = 1;
}
gspca_dev->nurbs = nurbs;
@@ -597,6 +613,12 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
if (ret < 0)
goto out;
+ /* clear the bulk endpoint */
+ if (gspca_dev->alt == 0) /* if bulk transfer */
+ usb_clear_halt(gspca_dev->dev,
+ usb_rcvintpipe(gspca_dev->dev,
+ gspca_dev->cam.epaddr));
+
/* start the cam */
ret = gspca_dev->sd_desc->start(gspca_dev);
if (ret < 0) {
@@ -604,10 +626,9 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
goto out;
}
gspca_dev->streaming = 1;
- atomic_set(&gspca_dev->nevent, 0);
- /* bulk transfers are started by the subdriver */
- if (gspca_dev->alt == 0)
+ /* some bulk transfers are started by the subdriver */
+ if (gspca_dev->alt == 0 && gspca_dev->cam.bulk_nurbs == 0)
break;
/* submit the URBs */
@@ -618,8 +639,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
"usb_submit_urb [%d] err %d", n, ret);
gspca_dev->streaming = 0;
destroy_urbs(gspca_dev);
- if (ret == -ENOSPC)
+ if (ret == -ENOSPC) {
+ msleep(20); /* wait for kill
+ * complete */
break; /* try the previous alt */
+ }
goto out;
}
}
@@ -637,7 +661,7 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
if (ret < 0)
- PDEBUG(D_ERR|D_STREAM, "set interface 0 err %d", ret);
+ PDEBUG(D_ERR|D_STREAM, "set alt 0 err %d", ret);
return ret;
}
@@ -645,7 +669,6 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
static void gspca_stream_off(struct gspca_dev *gspca_dev)
{
gspca_dev->streaming = 0;
- atomic_set(&gspca_dev->nevent, 0);
if (gspca_dev->present
&& gspca_dev->sd_desc->stopN)
gspca_dev->sd_desc->stopN(gspca_dev);
@@ -727,7 +750,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
if (fmtdesc->index == index)
break; /* new format */
index++;
- if (index >= sizeof fmt_tb / sizeof fmt_tb[0])
+ if (index >= ARRAY_SIZE(fmt_tb))
return -EINVAL;
}
}
@@ -752,8 +775,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct gspca_dev *gspca_dev = priv;
int mode;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
mode = gspca_dev->curr_mode;
memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
sizeof fmt->fmt.pix);
@@ -765,8 +786,6 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
{
int w, h, mode, mode2;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
w = fmt->fmt.pix.width;
h = fmt->fmt.pix.height;
@@ -846,11 +865,11 @@ out:
return ret;
}
-static void gspca_delete(struct kref *kref)
+static void gspca_release(struct video_device *vfd)
{
- struct gspca_dev *gspca_dev = container_of(kref, struct gspca_dev, kref);
+ struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev);
- PDEBUG(D_STREAM, "device deleted");
+ PDEBUG(D_STREAM, "device released");
kfree(gspca_dev->usb_buf);
kfree(gspca_dev);
@@ -862,7 +881,7 @@ static int dev_open(struct inode *inode, struct file *file)
int ret;
PDEBUG(D_STREAM, "%s open", current->comm);
- gspca_dev = video_drvdata(file);
+ gspca_dev = (struct gspca_dev *) video_devdata(file);
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
if (!gspca_dev->present) {
@@ -883,17 +902,14 @@ static int dev_open(struct inode *inode, struct file *file)
gspca_dev->users++;
- /* one more user */
- kref_get(&gspca_dev->kref);
-
file->private_data = gspca_dev;
#ifdef GSPCA_DEBUG
/* activate the v4l2 debug */
if (gspca_debug & D_V4L2)
- gspca_dev->vdev->debug |= V4L2_DEBUG_IOCTL
+ gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL
| V4L2_DEBUG_IOCTL_ARG;
else
- gspca_dev->vdev->debug &= ~(V4L2_DEBUG_IOCTL
+ gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL
| V4L2_DEBUG_IOCTL_ARG);
#endif
ret = 0;
@@ -932,8 +948,6 @@ static int dev_close(struct inode *inode, struct file *file)
PDEBUG(D_STREAM, "close done");
- kref_put(&gspca_dev->kref, gspca_delete);
-
return 0;
}
@@ -1053,6 +1067,35 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
return -EINVAL;
}
+/*fixme: have an audio flag in gspca_dev?*/
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ if (audio->index != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ memset(audio, 0, sizeof *audio);
+ strcpy(audio->name, "Microphone");
+ return 0;
+}
+
+static int vidioc_enumaudio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ if (audio->index != 0)
+ return -EINVAL;
+
+ strcpy(audio->name, "Microphone");
+ audio->capability = 0;
+ audio->mode = 0;
+ return 0;
+}
+
static int vidioc_querymenu(struct file *file, void *priv,
struct v4l2_querymenu *qmenu)
{
@@ -1096,8 +1139,6 @@ static int vidioc_reqbufs(struct file *file, void *priv,
struct gspca_dev *gspca_dev = priv;
int i, ret = 0;
- if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
switch (rb->memory) {
case GSPCA_MEMORY_READ: /* (internal call) */
case V4L2_MEMORY_MMAP:
@@ -1162,8 +1203,7 @@ static int vidioc_querybuf(struct file *file, void *priv,
struct gspca_dev *gspca_dev = priv;
struct gspca_frame *frame;
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
- || v4l2_buf->index < 0
+ if (v4l2_buf->index < 0
|| v4l2_buf->index >= gspca_dev->nframes)
return -EINVAL;
@@ -1186,7 +1226,8 @@ static int vidioc_streamon(struct file *file, void *priv,
ret = -ENODEV;
goto out;
}
- if (gspca_dev->nframes == 0) {
+ if (gspca_dev->nframes == 0
+ || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) {
ret = -EINVAL;
goto out;
}
@@ -1236,7 +1277,6 @@ static int vidioc_streamoff(struct file *file, void *priv,
gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
gspca_dev->last_packet_type = DISCARD_PACKET;
gspca_dev->sequence = 0;
- atomic_set(&gspca_dev->nevent, 0);
ret = 0;
out:
mutex_unlock(&gspca_dev->queue_lock);
@@ -1281,6 +1321,17 @@ static int vidioc_g_parm(struct file *filp, void *priv,
memset(parm, 0, sizeof *parm);
parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm->parm.capture.readbuffers = gspca_dev->nbufread;
+
+ if (gspca_dev->sd_desc->get_streamparm) {
+ int ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ ret = gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+
return 0;
}
@@ -1295,6 +1346,17 @@ static int vidioc_s_parm(struct file *filp, void *priv,
parm->parm.capture.readbuffers = gspca_dev->nbufread;
else
gspca_dev->nbufread = n;
+
+ if (gspca_dev->sd_desc->set_streamparm) {
+ int ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ ret = gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+
return 0;
}
@@ -1440,33 +1502,22 @@ static int frame_wait(struct gspca_dev *gspca_dev,
i = gspca_dev->fr_o;
j = gspca_dev->fr_queue[i];
frame = &gspca_dev->frame[j];
- if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) {
- atomic_dec(&gspca_dev->nevent);
- goto ok;
- }
- if (nonblock_ing) /* no frame yet */
- return -EAGAIN;
- /* wait till a frame is ready */
- for (;;) {
+ if (!(frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE)) {
+ if (nonblock_ing)
+ return -EAGAIN;
+
+ /* wait till a frame is ready */
ret = wait_event_interruptible_timeout(gspca_dev->wq,
- atomic_read(&gspca_dev->nevent) > 0,
- msecs_to_jiffies(3000));
- if (ret <= 0) {
- if (ret < 0)
- return ret; /* interrupt */
- return -EIO; /* timeout */
- }
- atomic_dec(&gspca_dev->nevent);
- if (!gspca_dev->streaming || !gspca_dev->present)
+ (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) ||
+ !gspca_dev->streaming || !gspca_dev->present,
+ msecs_to_jiffies(3000));
+ if (ret < 0)
+ return ret;
+ if (ret == 0 || !gspca_dev->streaming || !gspca_dev->present)
return -EIO;
- i = gspca_dev->fr_o;
- j = gspca_dev->fr_queue[i];
- frame = &gspca_dev->frame[j];
- if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE)
- break;
}
-ok:
+
gspca_dev->fr_o = (i + 1) % gspca_dev->nframes;
PDEBUG(D_FRAM, "frame wait q:%d i:%d o:%d",
gspca_dev->fr_q,
@@ -1494,8 +1545,6 @@ static int vidioc_dqbuf(struct file *file, void *priv,
int i, ret;
PDEBUG(D_FRAM, "dqbuf");
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
if (v4l2_buf->memory != gspca_dev->memory)
return -EINVAL;
@@ -1550,8 +1599,6 @@ static int vidioc_qbuf(struct file *file, void *priv,
int i, index, ret;
PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index);
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
@@ -1761,7 +1808,7 @@ static struct file_operations dev_fops = {
.release = dev_close,
.read = dev_read,
.mmap = dev_mmap,
- .ioctl = video_ioctl2,
+ .unlocked_ioctl = __video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
#endif
@@ -1781,6 +1828,9 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = {
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_enumaudio = vidioc_enumaudio,
.vidioc_querymenu = vidioc_querymenu,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
@@ -1802,7 +1852,7 @@ static struct video_device gspca_template = {
.name = "gspca main driver",
.fops = &dev_fops,
.ioctl_ops = &dev_ioctl_ops,
- .release = video_device_release,
+ .release = gspca_release,
.minor = -1,
};
@@ -1840,7 +1890,6 @@ int gspca_dev_probe(struct usb_interface *intf,
err("couldn't kzalloc gspca struct");
return -ENOMEM;
}
- kref_init(&gspca_dev->kref);
gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
if (!gspca_dev->usb_buf) {
err("out of memory");
@@ -1852,12 +1901,13 @@ int gspca_dev_probe(struct usb_interface *intf,
gspca_dev->nbalt = intf->num_altsetting;
gspca_dev->sd_desc = sd_desc;
gspca_dev->nbufread = 2;
+ gspca_dev->empty_packet = -1; /* don't check the empty packets */
/* configure the subdriver and initialize the USB device */
- ret = gspca_dev->sd_desc->config(gspca_dev, id);
+ ret = sd_desc->config(gspca_dev, id);
if (ret < 0)
goto out;
- ret = gspca_dev->sd_desc->init(gspca_dev);
+ ret = sd_desc->init(gspca_dev);
if (ret < 0)
goto out;
ret = gspca_set_alt0(gspca_dev);
@@ -1871,18 +1921,15 @@ int gspca_dev_probe(struct usb_interface *intf,
init_waitqueue_head(&gspca_dev->wq);
/* init video stuff */
- gspca_dev->vdev = video_device_alloc();
- memcpy(gspca_dev->vdev, &gspca_template, sizeof gspca_template);
- gspca_dev->vdev->parent = &dev->dev;
+ memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template);
+ gspca_dev->vdev.parent = &dev->dev;
gspca_dev->module = module;
gspca_dev->present = 1;
- video_set_drvdata(gspca_dev->vdev, gspca_dev);
- ret = video_register_device(gspca_dev->vdev,
+ ret = video_register_device(&gspca_dev->vdev,
VFL_TYPE_GRABBER,
video_nr);
if (ret < 0) {
err("video_register_device err %d", ret);
- video_device_release(gspca_dev->vdev);
goto out;
}
@@ -1906,15 +1953,14 @@ void gspca_disconnect(struct usb_interface *intf)
{
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
- usb_set_intfdata(intf, NULL);
-
-/* We don't want people trying to open up the device */
- video_unregister_device(gspca_dev->vdev);
-
gspca_dev->present = 0;
gspca_dev->streaming = 0;
- kref_put(&gspca_dev->kref, gspca_delete);
+ usb_set_intfdata(intf, NULL);
+
+ /* release the device */
+ /* (this will call gspca_release() immediatly or on last close) */
+ video_unregister_device(&gspca_dev->vdev);
PDEBUG(D_PROBE, "disconnect complete");
}
@@ -1992,7 +2038,7 @@ int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
desired lumination fast (with the risc of a slight overshoot) */
steps = abs(desired_avg_lum - avg_lum) / deadzone;
- PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n",
+ PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
avg_lum, desired_avg_lum, steps);
for (i = 0; i < steps; i++) {
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
index d25e8d69373b..c90af9cb1e07 100644
--- a/drivers/media/video/gspca/gspca.h
+++ b/drivers/media/video/gspca/gspca.h
@@ -56,8 +56,12 @@ extern int gspca_debug;
/* device information - set at probe time */
struct cam {
int bulk_size; /* buffer size when image transfer by bulk */
- struct v4l2_pix_format *cam_mode; /* size nmodes */
+ const struct v4l2_pix_format *cam_mode; /* size nmodes */
char nmodes;
+ __u8 bulk_nurbs; /* number of URBs in bulk mode
+ * - cannot be > MAX_NURBS
+ * - when 0 and bulk_size != 0 means
+ * 1 URB and submit done by subdriver */
__u8 epaddr;
};
@@ -70,6 +74,8 @@ typedef void (*cam_v_op) (struct gspca_dev *);
typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *);
typedef int (*cam_jpg_op) (struct gspca_dev *,
struct v4l2_jpegcompression *);
+typedef int (*cam_streamparm_op) (struct gspca_dev *,
+ struct v4l2_streamparm *);
typedef int (*cam_qmnu_op) (struct gspca_dev *,
struct v4l2_querymenu *);
typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
@@ -102,6 +108,8 @@ struct sd_desc {
cam_jpg_op get_jcomp;
cam_jpg_op set_jcomp;
cam_qmnu_op querymenu;
+ cam_streamparm_op get_streamparm;
+ cam_streamparm_op set_streamparm;
};
/* packet types when moving from iso buf to frame buf */
@@ -120,10 +128,9 @@ struct gspca_frame {
};
struct gspca_dev {
- struct video_device *vdev;
+ struct video_device vdev; /* !! must be the first item */
struct module *module; /* subdriver handling the device */
struct usb_device *dev;
- struct kref kref;
struct file *capt_file; /* file doing video capture */
struct cam cam; /* device information */
@@ -142,22 +149,20 @@ struct gspca_dev {
char fr_q; /* next frame to queue */
char fr_o; /* next frame to dequeue */
signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */
- char last_packet_type;
+ __u8 last_packet_type;
+ __s8 empty_packet; /* if (-1) don't check empty packets */
+ __u8 streaming;
- __u8 iface; /* USB interface number */
- __u8 alt; /* USB alternate setting */
__u8 curr_mode; /* current camera mode */
__u32 pixfmt; /* current mode parameters */
__u16 width;
__u16 height;
+ __u32 sequence; /* frame sequence number */
- atomic_t nevent; /* number of frames done */
wait_queue_head_t wq; /* wait queue */
struct mutex usb_lock; /* usb exchange protection */
struct mutex read_lock; /* read protection */
struct mutex queue_lock; /* ISOC queue protection */
- __u32 sequence; /* frame sequence number */
- char streaming;
#ifdef CONFIG_PM
char frozen; /* suspend - resume */
#endif
@@ -166,6 +171,8 @@ struct gspca_dev {
char nbufread; /* number of buffers for read() */
char nurbs; /* number of allocated URBs */
char memory; /* memory type (V4L2_MEMORY_xxx) */
+ __u8 iface; /* USB interface number */
+ __u8 alt; /* USB alternate setting */
__u8 nbalt; /* number of USB alternate settings */
};
diff --git a/drivers/media/video/gspca/m5602/m5602_bridge.h b/drivers/media/video/gspca/m5602/m5602_bridge.h
index 1a37ae4bc82d..a3f3b7a0c7e7 100644
--- a/drivers/media/video/gspca/m5602/m5602_bridge.h
+++ b/drivers/media/video/gspca/m5602/m5602_bridge.h
@@ -25,59 +25,59 @@
/*****************************************************************************/
-#define M5602_XB_SENSOR_TYPE 0x00
-#define M5602_XB_SENSOR_CTRL 0x01
-#define M5602_XB_LINE_OF_FRAME_H 0x02
-#define M5602_XB_LINE_OF_FRAME_L 0x03
-#define M5602_XB_PIX_OF_LINE_H 0x04
-#define M5602_XB_PIX_OF_LINE_L 0x05
-#define M5602_XB_VSYNC_PARA 0x06
-#define M5602_XB_HSYNC_PARA 0x07
-#define M5602_XB_TEST_MODE_1 0x08
-#define M5602_XB_TEST_MODE_2 0x09
-#define M5602_XB_SIG_INI 0x0a
-#define M5602_XB_DS_PARA 0x0e
-#define M5602_XB_TRIG_PARA 0x0f
-#define M5602_XB_CLK_PD 0x10
-#define M5602_XB_MCU_CLK_CTRL 0x12
-#define M5602_XB_MCU_CLK_DIV 0x13
-#define M5602_XB_SEN_CLK_CTRL 0x14
-#define M5602_XB_SEN_CLK_DIV 0x15
-#define M5602_XB_AUD_CLK_CTRL 0x16
-#define M5602_XB_AUD_CLK_DIV 0x17
-#define M5602_XB_DEVCTR1 0x41
-#define M5602_XB_EPSETR0 0x42
-#define M5602_XB_EPAFCTR 0x47
-#define M5602_XB_EPBFCTR 0x49
-#define M5602_XB_EPEFCTR 0x4f
-#define M5602_XB_TEST_REG 0x53
-#define M5602_XB_ALT2SIZE 0x54
-#define M5602_XB_ALT3SIZE 0x55
-#define M5602_XB_OBSFRAME 0x56
-#define M5602_XB_PWR_CTL 0x59
-#define M5602_XB_ADC_CTRL 0x60
-#define M5602_XB_ADC_DATA 0x61
-#define M5602_XB_MISC_CTRL 0x62
-#define M5602_XB_SNAPSHOT 0x63
-#define M5602_XB_SCRATCH_1 0x64
-#define M5602_XB_SCRATCH_2 0x65
-#define M5602_XB_SCRATCH_3 0x66
-#define M5602_XB_SCRATCH_4 0x67
-#define M5602_XB_I2C_CTRL 0x68
-#define M5602_XB_I2C_CLK_DIV 0x69
-#define M5602_XB_I2C_DEV_ADDR 0x6a
-#define M5602_XB_I2C_REG_ADDR 0x6b
-#define M5602_XB_I2C_DATA 0x6c
-#define M5602_XB_I2C_STATUS 0x6d
-#define M5602_XB_GPIO_DAT_H 0x70
-#define M5602_XB_GPIO_DAT_L 0x71
-#define M5602_XB_GPIO_DIR_H 0x72
-#define M5602_XB_GPIO_DIR_L 0x73
-#define M5602_XB_GPIO_EN_H 0x74
-#define M5602_XB_GPIO_EN_L 0x75
-#define M5602_XB_GPIO_DAT 0x76
-#define M5602_XB_GPIO_DIR 0x77
-#define M5602_XB_MISC_CTL 0x70
+#define M5602_XB_SENSOR_TYPE 0x00
+#define M5602_XB_SENSOR_CTRL 0x01
+#define M5602_XB_LINE_OF_FRAME_H 0x02
+#define M5602_XB_LINE_OF_FRAME_L 0x03
+#define M5602_XB_PIX_OF_LINE_H 0x04
+#define M5602_XB_PIX_OF_LINE_L 0x05
+#define M5602_XB_VSYNC_PARA 0x06
+#define M5602_XB_HSYNC_PARA 0x07
+#define M5602_XB_TEST_MODE_1 0x08
+#define M5602_XB_TEST_MODE_2 0x09
+#define M5602_XB_SIG_INI 0x0a
+#define M5602_XB_DS_PARA 0x0e
+#define M5602_XB_TRIG_PARA 0x0f
+#define M5602_XB_CLK_PD 0x10
+#define M5602_XB_MCU_CLK_CTRL 0x12
+#define M5602_XB_MCU_CLK_DIV 0x13
+#define M5602_XB_SEN_CLK_CTRL 0x14
+#define M5602_XB_SEN_CLK_DIV 0x15
+#define M5602_XB_AUD_CLK_CTRL 0x16
+#define M5602_XB_AUD_CLK_DIV 0x17
+#define M5602_XB_DEVCTR1 0x41
+#define M5602_XB_EPSETR0 0x42
+#define M5602_XB_EPAFCTR 0x47
+#define M5602_XB_EPBFCTR 0x49
+#define M5602_XB_EPEFCTR 0x4f
+#define M5602_XB_TEST_REG 0x53
+#define M5602_XB_ALT2SIZE 0x54
+#define M5602_XB_ALT3SIZE 0x55
+#define M5602_XB_OBSFRAME 0x56
+#define M5602_XB_PWR_CTL 0x59
+#define M5602_XB_ADC_CTRL 0x60
+#define M5602_XB_ADC_DATA 0x61
+#define M5602_XB_MISC_CTRL 0x62
+#define M5602_XB_SNAPSHOT 0x63
+#define M5602_XB_SCRATCH_1 0x64
+#define M5602_XB_SCRATCH_2 0x65
+#define M5602_XB_SCRATCH_3 0x66
+#define M5602_XB_SCRATCH_4 0x67
+#define M5602_XB_I2C_CTRL 0x68
+#define M5602_XB_I2C_CLK_DIV 0x69
+#define M5602_XB_I2C_DEV_ADDR 0x6a
+#define M5602_XB_I2C_REG_ADDR 0x6b
+#define M5602_XB_I2C_DATA 0x6c
+#define M5602_XB_I2C_STATUS 0x6d
+#define M5602_XB_GPIO_DAT_H 0x70
+#define M5602_XB_GPIO_DAT_L 0x71
+#define M5602_XB_GPIO_DIR_H 0x72
+#define M5602_XB_GPIO_DIR_L 0x73
+#define M5602_XB_GPIO_EN_H 0x74
+#define M5602_XB_GPIO_EN_L 0x75
+#define M5602_XB_GPIO_DAT 0x76
+#define M5602_XB_GPIO_DIR 0x77
+#define M5602_XB_MISC_CTL 0x70
#define I2C_BUSY 0x80
@@ -90,13 +90,7 @@
#define M5602_ISOC_ENDPOINT_ADDR 0x81
#define M5602_INTR_ENDPOINT_ADDR 0x82
-#define M5602_MAX_FRAMES 32
-#define M5602_URBS 2
-#define M5602_ISOC_PACKETS 14
-
-#define M5602_URB_TIMEOUT msecs_to_jiffies(2 * M5602_ISOC_PACKETS)
#define M5602_URB_MSG_TIMEOUT 5000
-#define M5602_FRAME_TIMEOUT 2
/*****************************************************************************/
@@ -115,7 +109,6 @@ static const unsigned char sensor_urb_skeleton[] = {
0x13, M5602_XB_I2C_CTRL, 0x81, 0x11
};
-/* m5602 device descriptor, currently it just wraps the m5602_camera struct */
struct sd {
struct gspca_dev gspca_dev;
@@ -140,4 +133,10 @@ int m5602_read_bridge(
int m5602_write_bridge(
struct sd *sd, u8 address, u8 i2c_data);
+int m5602_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+int m5602_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c
index fd6ce384b487..ed906fe31287 100644
--- a/drivers/media/video/gspca/m5602/m5602_core.c
+++ b/drivers/media/video/gspca/m5602/m5602_core.c
@@ -24,7 +24,7 @@
/* Kernel module parameters */
int force_sensor;
-int dump_bridge;
+static int dump_bridge;
int dump_sensor;
static const __devinitdata struct usb_device_id m5602_table[] = {
@@ -80,6 +80,97 @@ int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data)
return (err < 0) ? err : 0;
}
+int m5602_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+
+ if (!len || len > sd->sensor->i2c_regW)
+ return -EINVAL;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ sd->sensor->i2c_slave_id);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ if (err < 0)
+ goto out;
+
+ if (sd->sensor->i2c_regW == 1) {
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, len);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
+ if (err < 0)
+ goto out;
+ } else {
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
+ if (err < 0)
+ goto out;
+ }
+
+ for (i = 0; (i < len) && !err; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(D_CONF, "Reading sensor register "
+ "0x%x containing 0x%x ", address, *i2c_data);
+ }
+out:
+ return err;
+}
+
+int m5602_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* No sensor with a data width larger than 16 bits has yet been seen */
+ if (len > sd->sensor->i2c_regW || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton,
+ sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ /* Special case larger sensor writes */
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the tailer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
/* Dump all the registers of the m5602 bridge,
unfortunately this breaks the camera until it's power cycled */
static void m5602_dump_bridge(struct sd *sd)
@@ -150,11 +241,15 @@ static int m5602_start_transfer(struct gspca_dev *gspca_dev)
/* Send start command to the camera */
const u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01};
+
+ if (sd->sensor->start)
+ sd->sensor->start(sd);
+
memcpy(buf, buffer, sizeof(buffer));
err = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
0x04, 0x40, 0x19, 0x0000, buf,
- 4, M5602_URB_MSG_TIMEOUT);
+ sizeof(buffer), M5602_URB_MSG_TIMEOUT);
PDEBUG(D_STREAM, "Transfer started");
return (err < 0) ? err : 0;
@@ -284,6 +379,7 @@ static int __init mod_m5602_init(void)
PDEBUG(D_PROBE, "registered");
return 0;
}
+
static void __exit mod_m5602_exit(void)
{
usb_deregister(&sd_driver);
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
index fb700c2d055a..c0e71c331454 100644
--- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
@@ -18,6 +18,8 @@
#include "m5602_mt9m111.h"
+static void mt9m111_dump_registers(struct sd *sd);
+
int mt9m111_probe(struct sd *sd)
{
u8 data[2] = {0x00, 0x00};
@@ -44,12 +46,12 @@ int mt9m111_probe(struct sd *sd)
} else {
data[0] = preinit_mt9m111[i][2];
data[1] = preinit_mt9m111[i][3];
- mt9m111_write_sensor(sd,
+ m5602_write_sensor(sd,
preinit_mt9m111[i][1], data, 2);
}
}
- if (mt9m111_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
+ if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
return -ENODEV;
if ((data[0] == 0x14) && (data[1] == 0x3a)) {
@@ -72,7 +74,7 @@ int mt9m111_init(struct sd *sd)
int i, err = 0;
/* Init the sensor */
- for (i = 0; i < ARRAY_SIZE(init_mt9m111); i++) {
+ for (i = 0; i < ARRAY_SIZE(init_mt9m111) && !err; i++) {
u8 data[2];
if (init_mt9m111[i][0] == BRIDGE) {
@@ -82,7 +84,7 @@ int mt9m111_init(struct sd *sd)
} else {
data[0] = init_mt9m111[i][2];
data[1] = init_mt9m111[i][3];
- err = mt9m111_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_mt9m111[i][1], data, 2);
}
}
@@ -104,12 +106,12 @@ int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2] = {0x00, 0x00};
struct sd *sd = (struct sd *) gspca_dev;
- err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
*val = data[0] & MT9M111_RMB_MIRROR_ROWS;
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -121,19 +123,19 @@ int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
/* Set the correct page map */
- err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
if (err < 0)
goto out;
- err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
if (err < 0)
goto out;
data[0] = (data[0] & 0xfe) | val;
- err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -142,12 +144,12 @@ int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2] = {0x00, 0x00};
struct sd *sd = (struct sd *) gspca_dev;
- err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
*val = data[0] & MT9M111_RMB_MIRROR_COLS;
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -159,19 +161,19 @@ int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
/* Set the correct page map */
- err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
if (err < 0)
goto out;
- err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
if (err < 0)
goto out;
data[0] = (data[0] & 0xfd) | ((val << 1) & 0x02);
- err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -180,7 +182,7 @@ int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2] = {0x00, 0x00};
struct sd *sd = (struct sd *) gspca_dev;
- err = mt9m111_read_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2);
+ err = m5602_read_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2);
tmp = ((data[1] << 8) | data[0]);
*val = ((tmp & (1 << 10)) * 2) |
@@ -190,7 +192,7 @@ int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
PDEBUG(D_V4L2, "Read gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -200,7 +202,7 @@ int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
/* Set the correct page map */
- err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
if (err < 0)
goto out;
@@ -225,90 +227,13 @@ int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "tmp=%d, data[1]=%d, data[0]=%d", tmp,
data[1], data[0]);
- err = mt9m111_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN,
+ err = m5602_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN,
data, 2);
out:
- return (err < 0) ? err : 0;
-}
-
-int mt9m111_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len) {
- int err, i;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- sd->sensor->i2c_slave_id);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x1a);
- if (err < 0)
- goto out;
-
- for (i = 0; i < len && !err; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x contains 0x%x ", address, *i2c_data);
- }
-out:
- return (err < 0) ? err : 0;
-}
-
-int mt9m111_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* No sensor with a data width larger
- than 16 bits has yet been seen, nor with 0 :p*/
- if (len > 2 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton,
- sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the tailer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
+ return err;
}
-void mt9m111_dump_registers(struct sd *sd)
+static void mt9m111_dump_registers(struct sd *sd)
{
u8 address, value[2] = {0x00, 0x00};
@@ -316,27 +241,27 @@ void mt9m111_dump_registers(struct sd *sd)
info("Dumping the mt9m111 sensor core registers");
value[1] = MT9M111_SENSOR_CORE;
- mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
- mt9m111_read_sensor(sd, address, value, 2);
+ m5602_read_sensor(sd, address, value, 2);
info("register 0x%x contains 0x%x%x",
address, value[0], value[1]);
}
info("Dumping the mt9m111 color pipeline registers");
value[1] = MT9M111_COLORPIPE;
- mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
- mt9m111_read_sensor(sd, address, value, 2);
+ m5602_read_sensor(sd, address, value, 2);
info("register 0x%x contains 0x%x%x",
address, value[0], value[1]);
}
info("Dumping the mt9m111 camera control registers");
value[1] = MT9M111_CAMERA_CONTROL;
- mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
- mt9m111_read_sensor(sd, address, value, 2);
+ m5602_read_sensor(sd, address, value, 2);
info("register 0x%x contains 0x%x%x",
address, value[0], value[1]);
}
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/drivers/media/video/gspca/m5602/m5602_mt9m111.h
index 315209d5aeef..e795ab7a36c9 100644
--- a/drivers/media/video/gspca/m5602/m5602_mt9m111.h
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.h
@@ -87,14 +87,6 @@ int mt9m111_probe(struct sd *sd);
int mt9m111_init(struct sd *sd);
int mt9m111_power_down(struct sd *sd);
-int mt9m111_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
-int mt9m111_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
-void mt9m111_dump_registers(struct sd *sd);
-
int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
@@ -106,14 +98,12 @@ static struct m5602_sensor mt9m111 = {
.name = "MT9M111",
.i2c_slave_id = 0xba,
+ .i2c_regW = 2,
.probe = mt9m111_probe,
.init = mt9m111_init,
.power_down = mt9m111_power_down,
- .read_sensor = mt9m111_read_sensor,
- .write_sensor = mt9m111_write_sensor,
-
.nctrls = 3,
.ctrls = {
{
@@ -1003,7 +993,7 @@ static const unsigned char init_mt9m111[][4] =
{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, /* 639*/
{BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c
index 837c7e47661c..c908a8d6970a 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov9650.c
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c
@@ -18,77 +18,57 @@
#include "m5602_ov9650.h"
-int ov9650_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
-
- /* The ov9650 registers have a max depth of one byte */
- if (len > 1 || !len)
- return -EINVAL;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
-
- m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- ov9650.i2c_slave_id);
- m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len);
- m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
-
- for (i = 0; i < len; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x containing 0x%x ", address, *i2c_data);
- }
- return (err < 0) ? err : 0;
-}
-
-int ov9650_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* The ov9650 only supports one byte writes */
- if (len > 1 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton,
- sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- /* Special case larger sensor writes */
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the tailer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+/* Vertically and horizontally flips the image if matched, needed for machines
+ where the sensor is mounted upside down */
+static
+ const
+ struct dmi_system_id ov9650_flip_dmi_table[] = {
+ {
+ .ident = "ASUS A6VC",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")
+ }
+ },
+ {
+ .ident = "ASUS A6VM",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")
+ }
+ },
+ {
+ .ident = "ASUS A6JC",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")
+ }
+ },
+ {
+ .ident = "ASUS A6Ja",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6J")
+ }
+ },
+ {
+ .ident = "ASUS A6Kt",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")
+ }
+ },
+ {
+ .ident = "Alienware Aurora m9700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700")
+ }
+ },
+ { }
+};
- return (err < 0) ? err : 0;
-}
+static void ov9650_dump_registers(struct sd *sd);
int ov9650_probe(struct sd *sd)
{
@@ -110,16 +90,16 @@ int ov9650_probe(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(preinit_ov9650); i++) {
u8 data = preinit_ov9650[i][2];
if (preinit_ov9650[i][0] == SENSOR)
- ov9650_write_sensor(sd,
+ m5602_write_sensor(sd,
preinit_ov9650[i][1], &data, 1);
else
m5602_write_bridge(sd, preinit_ov9650[i][1], data);
}
- if (ov9650_read_sensor(sd, OV9650_PID, &prod_id, 1))
+ if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1))
return -ENODEV;
- if (ov9650_read_sensor(sd, OV9650_VER, &ver_id, 1))
+ if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1))
return -ENODEV;
if ((prod_id == 0x96) && (ver_id == 0x52)) {
@@ -148,34 +128,90 @@ int ov9650_init(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) {
data = init_ov9650[i][2];
if (init_ov9650[i][0] == SENSOR)
- err = ov9650_write_sensor(sd, init_ov9650[i][1],
+ err = m5602_write_sensor(sd, init_ov9650[i][1],
&data, 1);
else
err = m5602_write_bridge(sd, init_ov9650[i][1], data);
}
- if (!err && dmi_check_system(ov9650_flip_dmi_table)) {
+ if (dmi_check_system(ov9650_flip_dmi_table) && !err) {
info("vflip quirk active");
data = 0x30;
- err = ov9650_write_sensor(sd, OV9650_MVFP, &data, 1);
+ err = m5602_write_sensor(sd, OV9650_MVFP, &data, 1);
}
+ return err;
+}
+
+int ov9650_start(struct sd *sd)
+{
+ int i, err = 0;
+ struct cam *cam = &sd->gspca_dev.cam;
- return (err < 0) ? err : 0;
+ for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) {
+ u8 data = res_init_ov9650[i][1];
+ err = m5602_write_bridge(sd, res_init_ov9650[i][0], data);
+ }
+ if (err < 0)
+ return err;
+
+ switch (cam->cam_mode[sd->gspca_dev.curr_mode].width)
+ {
+ case 640:
+ PDEBUG(D_V4L2, "Configuring camera for VGA mode");
+
+ for (i = 0; i < ARRAY_SIZE(VGA_ov9650) && !err; i++) {
+ u8 data = VGA_ov9650[i][2];
+ if (VGA_ov9650[i][0] == SENSOR)
+ err = m5602_write_sensor(sd,
+ VGA_ov9650[i][1], &data, 1);
+ else
+ err = m5602_write_bridge(sd, VGA_ov9650[i][1], data);
+ }
+ break;
+
+ case 352:
+ PDEBUG(D_V4L2, "Configuring camera for CIF mode");
+
+ for (i = 0; i < ARRAY_SIZE(CIF_ov9650) && !err; i++) {
+ u8 data = CIF_ov9650[i][2];
+ if (CIF_ov9650[i][0] == SENSOR)
+ err = m5602_write_sensor(sd,
+ CIF_ov9650[i][1], &data, 1);
+ else
+ err = m5602_write_bridge(sd, CIF_ov9650[i][1], data);
+ }
+ break;
+
+ case 320:
+ PDEBUG(D_V4L2, "Configuring camera for QVGA mode");
+
+ for (i = 0; i < ARRAY_SIZE(QVGA_ov9650) && !err; i++) {
+ u8 data = QVGA_ov9650[i][2];
+ if (QVGA_ov9650[i][0] == SENSOR)
+ err = m5602_write_sensor(sd,
+ QVGA_ov9650[i][1], &data, 1);
+ else
+ err = m5602_write_bridge(sd, QVGA_ov9650[i][1], data);
+ }
+ break;
+ }
+ return err;
}
int ov9650_power_down(struct sd *sd)
{
- int i;
- for (i = 0; i < ARRAY_SIZE(power_down_ov9650); i++) {
+ int i, err = 0;
+ for (i = 0; i < ARRAY_SIZE(power_down_ov9650) && !err; i++) {
u8 data = power_down_ov9650[i][2];
if (power_down_ov9650[i][0] == SENSOR)
- ov9650_write_sensor(sd,
+ err = m5602_write_sensor(sd,
power_down_ov9650[i][1], &data, 1);
else
- m5602_write_bridge(sd, power_down_ov9650[i][1], data);
+ err = m5602_write_bridge(sd, power_down_ov9650[i][1],
+ data);
}
- return 0;
+ return err;
}
int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
@@ -184,24 +220,24 @@ int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = ov9650_read_sensor(sd, OV9650_COM1, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM1, &i2c_data, 1);
if (err < 0)
goto out;
*val = i2c_data & 0x03;
- err = ov9650_read_sensor(sd, OV9650_AECH, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_AECH, &i2c_data, 1);
if (err < 0)
goto out;
*val |= (i2c_data << 2);
- err = ov9650_read_sensor(sd, OV9650_AECHM, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_AECHM, &i2c_data, 1);
if (err < 0)
goto out;
*val |= (i2c_data & 0x3f) << 10;
PDEBUG(D_V4L2, "Read exposure %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
@@ -215,24 +251,24 @@ int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
/* The 6 MSBs */
i2c_data = (val >> 10) & 0x3f;
- err = ov9650_write_sensor(sd, OV9650_AECHM,
+ err = m5602_write_sensor(sd, OV9650_AECHM,
&i2c_data, 1);
if (err < 0)
goto out;
/* The 8 middle bits */
i2c_data = (val >> 2) & 0xff;
- err = ov9650_write_sensor(sd, OV9650_AECH,
+ err = m5602_write_sensor(sd, OV9650_AECH,
&i2c_data, 1);
if (err < 0)
goto out;
/* The 2 LSBs */
i2c_data = val & 0x03;
- err = ov9650_write_sensor(sd, OV9650_COM1, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -241,13 +277,13 @@ int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
*val = (i2c_data & 0x03) << 8;
- err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
*val |= i2c_data;
PDEBUG(D_V4L2, "Read gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -259,16 +295,16 @@ int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
/* The 2 MSB */
/* Read the OV9650_VREF register first to avoid
corrupting the VREF high and low bits */
- ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
/* Mask away all uninteresting bits */
i2c_data = ((val & 0x0300) >> 2) |
(i2c_data & 0x3F);
- err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
/* The 8 LSBs */
i2c_data = val & 0xff;
- err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
- return (err < 0) ? err : 0;
+ err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ return err;
}
int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -277,12 +313,12 @@ int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_RED, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_RED, &i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read red gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -295,9 +331,9 @@ int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
val & 0xff);
i2c_data = val & 0xff;
- err = ov9650_write_sensor(sd, OV9650_RED, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -306,12 +342,12 @@ int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_BLUE, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_BLUE, &i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read blue gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -324,9 +360,9 @@ int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
val & 0xff);
i2c_data = val & 0xff;
- err = ov9650_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -335,14 +371,14 @@ int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
if (dmi_check_system(ov9650_flip_dmi_table))
*val = ((i2c_data & OV9650_HFLIP) >> 5) ? 0 : 1;
else
*val = (i2c_data & OV9650_HFLIP) >> 5;
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -352,20 +388,20 @@ int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
- err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
if (err < 0)
goto out;
if (dmi_check_system(ov9650_flip_dmi_table))
i2c_data = ((i2c_data & 0xdf) |
- (((val ? 0 : 1) & 0x01) << 5));
+ (((val ? 0 : 1) & 0x01) << 5));
else
i2c_data = ((i2c_data & 0xdf) |
- ((val & 0x01) << 5));
+ ((val & 0x01) << 5));
- err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -374,14 +410,14 @@ int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
if (dmi_check_system(ov9650_flip_dmi_table))
*val = ((i2c_data & 0x10) >> 4) ? 0 : 1;
else
*val = (i2c_data & 0x10) >> 4;
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -391,7 +427,7 @@ int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
- err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
if (err < 0)
goto out;
@@ -402,9 +438,9 @@ int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
i2c_data = ((i2c_data & 0xef) |
((val & 0x01) << 4));
- err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -413,16 +449,16 @@ int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
if (err < 0)
goto out;
*val = (i2c_data & 0x03) << 8;
- err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
*val |= i2c_data;
PDEBUG(D_V4L2, "Read gain %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -435,22 +471,22 @@ int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
/* Read the OV9650_VREF register first to avoid
corrupting the VREF high and low bits */
- err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
if (err < 0)
goto out;
/* Mask away all uninteresting bits */
i2c_data = ((val & 0x0300) >> 2) | (i2c_data & 0x3F);
- err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
if (err < 0)
goto out;
/* The 8 LSBs */
i2c_data = val & 0xff;
- err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -459,11 +495,11 @@ int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
*val = (i2c_data & OV9650_AWB_EN) >> 1;
PDEBUG(D_V4L2, "Read auto white balance %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -473,14 +509,14 @@ int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_V4L2, "Set auto white balance to %d", val);
- err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
if (err < 0)
goto out;
i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
- err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -489,11 +525,11 @@ int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
*val = (i2c_data & OV9650_AGC_EN) >> 2;
PDEBUG(D_V4L2, "Read auto gain control %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -503,23 +539,23 @@ int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_V4L2, "Set auto gain control to %d", val);
- err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
if (err < 0)
goto out;
i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
- err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
-void ov9650_dump_registers(struct sd *sd)
+static void ov9650_dump_registers(struct sd *sd)
{
int address;
info("Dumping the ov9650 register state");
for (address = 0; address < 0xa9; address++) {
u8 value;
- ov9650_read_sensor(sd, address, &value, 1);
+ m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
@@ -531,9 +567,9 @@ void ov9650_dump_registers(struct sd *sd)
u8 old_value, ctrl_value;
u8 test_value[2] = {0xff, 0xff};
- ov9650_read_sensor(sd, address, &old_value, 1);
- ov9650_write_sensor(sd, address, test_value, 1);
- ov9650_read_sensor(sd, address, &ctrl_value, 1);
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value[0])
info("register 0x%x is writeable", address);
@@ -541,6 +577,6 @@ void ov9650_dump_registers(struct sd *sd)
info("register 0x%x is read only", address);
/* Restore original value */
- ov9650_write_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &old_value, 1);
}
}
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.h b/drivers/media/video/gspca/m5602/m5602_ov9650.h
index 065632f0378e..f4b33b8e8dae 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov9650.h
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.h
@@ -20,7 +20,6 @@
#define M5602_OV9650_H_
#include <linux/dmi.h>
-
#include "m5602_sensor.h"
/*****************************************************************************/
@@ -36,6 +35,7 @@
#define OV9650_PID 0x0a
#define OV9650_VER 0x0b
#define OV9650_COM3 0x0c
+#define OV9650_COM4 0x0d
#define OV9650_COM5 0x0e
#define OV9650_COM6 0x0f
#define OV9650_AECH 0x10
@@ -94,6 +94,8 @@
#define OV9650_REGISTER_RESET (1 << 7)
#define OV9650_VGA_SELECT (1 << 6)
+#define OV9650_CIF_SELECT (1 << 5)
+#define OV9650_QVGA_SELECT (1 << 4)
#define OV9650_RGB_SELECT (1 << 2)
#define OV9650_RAW_RGB_SELECT (1 << 0)
@@ -108,6 +110,8 @@
#define OV9650_SYSTEM_CLK_SEL (1 << 7)
#define OV9650_SLAM_MODE (1 << 4)
+#define OV9650_QVGA_VARIOPIXEL (1 << 7)
+
#define OV9650_VFLIP (1 << 4)
#define OV9650_HFLIP (1 << 5)
@@ -124,15 +128,9 @@ extern int dump_sensor;
int ov9650_probe(struct sd *sd);
int ov9650_init(struct sd *sd);
+int ov9650_start(struct sd *sd);
int ov9650_power_down(struct sd *sd);
-int ov9650_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-int ov9650_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
-void ov9650_dump_registers(struct sd *sd);
-
int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
@@ -155,11 +153,11 @@ int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val);
static struct m5602_sensor ov9650 = {
.name = "OV9650",
.i2c_slave_id = 0x60,
+ .i2c_regW = 1,
.probe = ov9650_probe,
.init = ov9650_init,
+ .start = ov9650_start,
.power_down = ov9650_power_down,
- .read_sensor = ov9650_read_sensor,
- .write_sensor = ov9650_write_sensor,
.nctrls = 8,
.ctrls = {
@@ -264,18 +262,38 @@ static struct m5602_sensor ov9650 = {
}
},
- .nmodes = 1,
+ .nmodes = 3,
.modes = {
{
- M5602_DEFAULT_FRAME_WIDTH,
- M5602_DEFAULT_FRAME_HEIGHT,
+ 320,
+ 240,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 320 * 240,
+ .bytesperline = 320,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }, {
+ 352,
+ 288,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 352 * 288,
+ .bytesperline = 352,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }, {
+ 640,
+ 480,
V4L2_PIX_FMT_SBGGR8,
V4L2_FIELD_NONE,
.sizeimage =
- M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
- .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ 640 * 480,
+ .bytesperline = 640,
.colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1
+ .priv = 0
}
}
};
@@ -324,6 +342,7 @@ static const unsigned char init_ov9650[][3] =
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+
/* Reset chip */
{SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
/* Enable double clock */
@@ -331,8 +350,6 @@ static const unsigned char init_ov9650[][3] =
/* Do something out of spec with the power */
{SENSOR, OV9650_OFON, 0x40},
- /* Set QQVGA */
- {SENSOR, OV9650_COM1, 0x20},
/* Set fast AGC/AEC algorithm with unlimited step size */
{SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC |
OV9650_AEC_UNLIM_STEP_SIZE |
@@ -343,7 +360,7 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_ACOM38, 0x81},
/* Turn off color matrix coefficient double option */
{SENSOR, OV9650_COM16, 0x00},
- /* Enable color matrix for RGB/YUV, Delay Y channel,
+ /* Enable color matrix for RGB/YUV, Delay Y channel,
set output Y/UV delay to 1 */
{SENSOR, OV9650_COM13, 0x19},
/* Enable digital BLC, Set output mode to U Y V Y */
@@ -352,7 +369,7 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_COM24, 0x00},
/* Enable HREF and some out of spec things */
{SENSOR, OV9650_COM12, 0x73},
- /* Set all DBLC offset signs to positive and
+ /* Set all DBLC offset signs to positive and
do some out of spec stuff */
{SENSOR, OV9650_DBLC1, 0xdf},
{SENSOR, OV9650_COM21, 0x06},
@@ -364,7 +381,7 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_RSVD96, 0x04},
/* Enable full range output */
{SENSOR, OV9650_COM15, 0x0},
- /* Enable HREF at optical black, enable ADBLC bias,
+ /* Enable HREF at optical black, enable ADBLC bias,
enable ADBLC, reset timings at format change */
{SENSOR, OV9650_COM6, 0x4b},
/* Subtract 32 from the B channel bias */
@@ -385,7 +402,7 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_AEB, 0x5c},
/* Set the high and low limit nibbles to 3 */
{SENSOR, OV9650_VPT, 0xc3},
- /* Set the Automatic Gain Ceiling (AGC) to 128x,
+ /* Set the Automatic Gain Ceiling (AGC) to 128x,
drop VSYNC at frame drop,
limit exposure timing,
drop frame when the AEC step is larger than the exposure gap */
@@ -394,9 +411,9 @@ static const unsigned char init_ov9650[][3] =
and set PWDN to SLVS (slave mode vertical sync) */
{SENSOR, OV9650_COM10, 0x42},
/* Set horizontal column start high to default value */
- {SENSOR, OV9650_HSTART, 0x1a},
+ {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */
/* Set horizontal column end */
- {SENSOR, OV9650_HSTOP, 0xbf},
+ {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */
/* Complementing register to the two writes above */
{SENSOR, OV9650_HREF, 0xb2},
/* Set vertical row start high bits */
@@ -405,10 +422,6 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_VSTOP, 0x7e},
/* Set complementing vertical frame control */
{SENSOR, OV9650_VREF, 0x10},
- /* Set raw RGB output format with VGA resolution */
- {SENSOR, OV9650_COM7, OV9650_VGA_SELECT |
- OV9650_RGB_SELECT |
- OV9650_RAW_RGB_SELECT},
{SENSOR, OV9650_ADC, 0x04},
{SENSOR, OV9650_HV, 0x40},
/* Enable denoise, and white-pixel erase */
@@ -417,30 +430,15 @@ static const unsigned char init_ov9650[][3] =
/* Set the high bits of the exposure value */
{SENSOR, OV9650_AECH, ((EXPOSURE_DEFAULT & 0xff00) >> 8)},
+ /* Enable VARIOPIXEL */
+ {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
+ {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL},
+
/* Set the low bits of the exposure value */
{SENSOR, OV9650_COM1, (EXPOSURE_DEFAULT & 0xff)},
{SENSOR, OV9650_GAIN, GAIN_DEFAULT},
{SENSOR, OV9650_BLUE, BLUE_GAIN_DEFAULT},
{SENSOR, OV9650_RED, RED_GAIN_DEFAULT},
-
- {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
- {SENSOR, OV9650_COM5, OV9650_SYSTEM_CLK_SEL},
-
- {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},
- {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00},
- {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
- {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},
- {BRIDGE, M5602_XB_SIG_INI, 0x01},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x09},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0x5e},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0xde}
};
static const unsigned char power_down_ov9650[][3] =
@@ -460,43 +458,76 @@ static const unsigned char power_down_ov9650[][3] =
{BRIDGE, M5602_XB_GPIO_EN_L, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
- {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
};
-/* Vertically and horizontally flips the image if matched, needed for machines
- where the sensor is mounted upside down */
-static
- const
- struct dmi_system_id ov9650_flip_dmi_table[] = {
- {
- .ident = "ASUS A6VC",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")
- }
- },
- {
- .ident = "ASUS A6VM",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")
- }
- },
- {
- .ident = "ASUS A6JC",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")
- }
- },
- {
- .ident = "ASUS A6Kt",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")
- }
- },
- { }
+static const unsigned char res_init_ov9650[][2] =
+{
+ {M5602_XB_LINE_OF_FRAME_H, 0x82},
+ {M5602_XB_LINE_OF_FRAME_L, 0x00},
+ {M5602_XB_PIX_OF_LINE_H, 0x82},
+ {M5602_XB_PIX_OF_LINE_L, 0x00},
+ {M5602_XB_SIG_INI, 0x01}
+};
+
+static const unsigned char VGA_ov9650[][3] =
+{
+ /* Moves the view window in a vertical orientation */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x09},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0}, /* 480 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x62}, /* 98 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, /* 640 + 98 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0xe2},
+
+ {SENSOR, OV9650_COM7, OV9650_VGA_SELECT |
+ OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT},
+};
+
+static const unsigned char CIF_ov9650[][3] =
+{
+ /* Moves the view window in a vertical orientation */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x09},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x20}, /* 288 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x62}, /* 98 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x01}, /* 352 + 98 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0xc2},
+
+ {SENSOR, OV9650_COM7, OV9650_CIF_SELECT |
+ OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT},
+};
+
+static const unsigned char QVGA_ov9650[][3] =
+{
+ /* Moves the view window in a vertical orientation */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x08},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xf0}, /* 240 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x31}, /* 50 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x01}, /* 320 + 50 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x71},
+
+ {SENSOR, OV9650_COM7, OV9650_QVGA_SELECT |
+ OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT},
};
#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c
index d17ac52566e6..2e7fb91673cf 100644
--- a/drivers/media/video/gspca/m5602/m5602_po1030.c
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.c
@@ -18,6 +18,8 @@
#include "m5602_po1030.h"
+static void po1030_dump_registers(struct sd *sd);
+
int po1030_probe(struct sd *sd)
{
u8 prod_id = 0, ver_id = 0, i;
@@ -38,16 +40,16 @@ int po1030_probe(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
u8 data = preinit_po1030[i][2];
if (preinit_po1030[i][0] == SENSOR)
- po1030_write_sensor(sd,
+ m5602_write_sensor(sd,
preinit_po1030[i][1], &data, 1);
else
m5602_write_bridge(sd, preinit_po1030[i][1], data);
}
- if (po1030_read_sensor(sd, 0x3, &prod_id, 1))
+ if (m5602_read_sensor(sd, 0x3, &prod_id, 1))
return -ENODEV;
- if (po1030_read_sensor(sd, 0x4, &ver_id, 1))
+ if (m5602_read_sensor(sd, 0x4, &ver_id, 1))
return -ENODEV;
if ((prod_id == 0x02) && (ver_id == 0xef)) {
@@ -64,78 +66,12 @@ sensor_found:
return 0;
}
-int po1030_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
-
- m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- sd->sensor->i2c_slave_id);
- m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len);
- m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
-
- for (i = 0; i < len; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x containing 0x%x ", address, *i2c_data);
- }
- return (err < 0) ? err : 0;
-}
-
-int po1030_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* The po1030 only supports one byte writes */
- if (len > 1 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton, sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the footer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
-}
-
int po1030_init(struct sd *sd)
{
int i, err = 0;
/* Init the sensor */
- for (i = 0; i < ARRAY_SIZE(init_po1030); i++) {
+ for (i = 0; i < ARRAY_SIZE(init_po1030) && !err; i++) {
u8 data[2] = {0x00, 0x00};
switch (init_po1030[i][0]) {
@@ -147,16 +83,10 @@ int po1030_init(struct sd *sd)
case SENSOR:
data[0] = init_po1030[i][2];
- err = po1030_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_po1030[i][1], data, 1);
break;
- case SENSOR_LONG:
- data[0] = init_po1030[i][2];
- data[1] = init_po1030[i][3];
- err = po1030_write_sensor(sd,
- init_po1030[i][1], data, 2);
- break;
default:
info("Invalid stream command, exiting init");
return -EINVAL;
@@ -166,7 +96,7 @@ int po1030_init(struct sd *sd)
if (dump_sensor)
po1030_dump_registers(sd);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
@@ -175,19 +105,19 @@ int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_H,
+ err = m5602_read_sensor(sd, PO1030_REG_INTEGLINES_H,
&i2c_data, 1);
if (err < 0)
goto out;
*val = (i2c_data << 8);
- err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_M,
+ err = m5602_read_sensor(sd, PO1030_REG_INTEGLINES_M,
&i2c_data, 1);
*val |= i2c_data;
PDEBUG(D_V4L2, "Exposure read as %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
@@ -202,7 +132,7 @@ int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "Set exposure to high byte to 0x%x",
i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_H,
+ err = m5602_write_sensor(sd, PO1030_REG_INTEGLINES_H,
&i2c_data, 1);
if (err < 0)
goto out;
@@ -210,11 +140,11 @@ int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
i2c_data = (val & 0xff);
PDEBUG(D_V4L2, "Set exposure to low byte to 0x%x",
i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_M,
+ err = m5602_write_sensor(sd, PO1030_REG_INTEGLINES_M,
&i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -223,12 +153,12 @@ int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_GLOBALGAIN,
+ err = m5602_read_sensor(sd, PO1030_REG_GLOBALGAIN,
&i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read global gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -237,14 +167,14 @@ int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_CONTROL2,
+ err = m5602_read_sensor(sd, PO1030_REG_CONTROL2,
&i2c_data, 1);
*val = (i2c_data >> 7) & 0x01 ;
PDEBUG(D_V4L2, "Read hflip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -254,13 +184,17 @@ int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set hflip %d", val);
+ err = m5602_read_sensor(sd, PO1030_REG_CONTROL2, &i2c_data, 1);
+ if (err < 0)
+ goto out;
- i2c_data = (val & 0x01) << 7;
+ i2c_data = (0x7f & i2c_data) | ((val & 0x01) << 7);
- err = po1030_write_sensor(sd, PO1030_REG_CONTROL2,
- &i2c_data, 1);
+ err = m5602_write_sensor(sd, PO1030_REG_CONTROL2,
+ &i2c_data, 1);
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -269,14 +203,14 @@ int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_GLOBALGAIN,
+ err = m5602_read_sensor(sd, PO1030_REG_GLOBALGAIN,
&i2c_data, 1);
*val = (i2c_data >> 6) & 0x01;
PDEBUG(D_V4L2, "Read vflip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -286,13 +220,17 @@ int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set vflip %d", val);
+ err = m5602_read_sensor(sd, PO1030_REG_CONTROL2, &i2c_data, 1);
+ if (err < 0)
+ goto out;
- i2c_data = (val & 0x01) << 6;
+ i2c_data = (i2c_data & 0xbf) | ((val & 0x01) << 6);
- err = po1030_write_sensor(sd, PO1030_REG_CONTROL2,
- &i2c_data, 1);
+ err = m5602_write_sensor(sd, PO1030_REG_CONTROL2,
+ &i2c_data, 1);
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -303,9 +241,9 @@ int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set global gain to %d", i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_GLOBALGAIN,
+ err = m5602_write_sensor(sd, PO1030_REG_GLOBALGAIN,
&i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -314,11 +252,11 @@ int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_RED_GAIN,
+ err = m5602_read_sensor(sd, PO1030_REG_RED_GAIN,
&i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read red gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -329,9 +267,9 @@ int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set red gain to %d", i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_RED_GAIN,
+ err = m5602_write_sensor(sd, PO1030_REG_RED_GAIN,
&i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -340,12 +278,12 @@ int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_BLUE_GAIN,
+ err = m5602_read_sensor(sd, PO1030_REG_BLUE_GAIN,
&i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read blue gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -355,10 +293,10 @@ int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
int err;
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set blue gain to %d", i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_BLUE_GAIN,
+ err = m5602_write_sensor(sd, PO1030_REG_BLUE_GAIN,
&i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_power_down(struct sd *sd)
@@ -366,14 +304,14 @@ int po1030_power_down(struct sd *sd)
return 0;
}
-void po1030_dump_registers(struct sd *sd)
+static void po1030_dump_registers(struct sd *sd)
{
int address;
u8 value = 0;
info("Dumping the po1030 sensor core registers");
for (address = 0; address < 0x7f; address++) {
- po1030_read_sensor(sd, address, &value, 1);
+ m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
@@ -385,9 +323,9 @@ void po1030_dump_registers(struct sd *sd)
u8 old_value, ctrl_value;
u8 test_value[2] = {0xff, 0xff};
- po1030_read_sensor(sd, address, &old_value, 1);
- po1030_write_sensor(sd, address, test_value, 1);
- po1030_read_sensor(sd, address, &ctrl_value, 1);
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value[0])
info("register 0x%x is writeable", address);
@@ -395,6 +333,6 @@ void po1030_dump_registers(struct sd *sd)
info("register 0x%x is read only", address);
/* Restore original value */
- po1030_write_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &old_value, 1);
}
}
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.h b/drivers/media/video/gspca/m5602/m5602_po1030.h
index a0b75ff61d79..def39d5bcec6 100644
--- a/drivers/media/video/gspca/m5602/m5602_po1030.h
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.h
@@ -10,7 +10,7 @@
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
- * Register defines taken from Pascal Stangs Proxycon Armlib
+ * Register defines taken from Pascal Stangs Procyon Armlib
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -128,13 +128,6 @@ int po1030_probe(struct sd *sd);
int po1030_init(struct sd *sd);
int po1030_power_down(struct sd *sd);
-void po1030_dump_registers(struct sd *sd);
-
-int po1030_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-int po1030_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
@@ -152,6 +145,7 @@ static struct m5602_sensor po1030 = {
.name = "PO1030",
.i2c_slave_id = 0xdc,
+ .i2c_regW = 1,
.probe = po1030_probe,
.init = po1030_init,
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
index 14b1eac5b812..e564a61a72d7 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
@@ -18,6 +18,40 @@
#include "m5602_s5k4aa.h"
+static
+ const
+ struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
+ {
+ .ident = "Fujitsu-Siemens Amilo Xa 2528",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
+ }
+ }, {
+ .ident = "Fujitsu-Siemens Amilo Xi 2550",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
+ }
+ }, {
+ .ident = "MSI GX700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
+ DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
+ }
+ }, {
+ .ident = "MSI GX700/GX705/EX700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700")
+ }
+ },
+ { }
+};
+
+static void s5k4aa_dump_registers(struct sd *sd);
+
int s5k4aa_probe(struct sd *sd)
{
u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
@@ -49,7 +83,7 @@ int s5k4aa_probe(struct sd *sd)
case SENSOR:
data[0] = preinit_s5k4aa[i][2];
- err = s5k4aa_write_sensor(sd,
+ err = m5602_write_sensor(sd,
preinit_s5k4aa[i][1],
data, 1);
break;
@@ -57,7 +91,7 @@ int s5k4aa_probe(struct sd *sd)
case SENSOR_LONG:
data[0] = preinit_s5k4aa[i][2];
data[1] = preinit_s5k4aa[i][3];
- err = s5k4aa_write_sensor(sd,
+ err = m5602_write_sensor(sd,
preinit_s5k4aa[i][1],
data, 2);
break;
@@ -68,13 +102,14 @@ int s5k4aa_probe(struct sd *sd)
}
/* Test some registers, but we don't know their exact meaning yet */
- if (s5k4aa_read_sensor(sd, 0x00, prod_id, sizeof(prod_id)))
+ if (m5602_read_sensor(sd, 0x00, prod_id, sizeof(prod_id)))
return -ENODEV;
if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
return -ENODEV;
else
info("Detected a s5k4aa sensor");
+
sensor_found:
sd->gspca_dev.cam.cam_mode = s5k4aa.modes;
sd->gspca_dev.cam.nmodes = s5k4aa.nmodes;
@@ -84,90 +119,6 @@ sensor_found:
return 0;
}
-int s5k4aa_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- sd->sensor->i2c_slave_id);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
- if (err < 0)
- goto out;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
- if (err < 0)
- goto out;
-
- for (i = 0; (i < len) & !err; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x containing 0x%x ", address, *i2c_data);
- }
-out:
- return (err < 0) ? err : 0;
-}
-
-int s5k4aa_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* No sensor with a data width larger than 16 bits has yet been seen */
- if (len > 2 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton,
- sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- /* Special case larger sensor writes */
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the tailer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
-}
-
int s5k4aa_init(struct sd *sd)
{
int i, err = 0;
@@ -184,14 +135,14 @@ int s5k4aa_init(struct sd *sd)
case SENSOR:
data[0] = init_s5k4aa[i][2];
- err = s5k4aa_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_s5k4aa[i][1], data, 1);
break;
case SENSOR_LONG:
data[0] = init_s5k4aa[i][2];
data[1] = init_s5k4aa[i][3];
- err = s5k4aa_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_s5k4aa[i][1], data, 2);
break;
default:
@@ -206,21 +157,21 @@ int s5k4aa_init(struct sd *sd)
if (!err && dmi_check_system(s5k4aa_vflip_dmi_table)) {
u8 data = 0x02;
info("vertical flip quirk active");
- s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
- s5k4aa_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
data |= S5K4AA_RM_V_FLIP;
data &= ~S5K4AA_RM_H_FLIP;
- s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
/* Decrement COLSTART to preserve color order (BGGR) */
- s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
data--;
- s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
/* Increment ROWSTART to preserve color order (BGGR) */
- s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
data++;
- s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
}
return (err < 0) ? err : 0;
@@ -237,20 +188,20 @@ int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
u8 data = S5K4AA_PAGE_MAP_2;
int err;
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
if (err < 0)
goto out;
*val = data << 8;
- err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
*val |= data;
PDEBUG(D_V4L2, "Read exposure %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
@@ -260,17 +211,17 @@ int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set exposure to %d", val);
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
data = (val >> 8) & 0xff;
- err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
if (err < 0)
goto out;
data = val & 0xff;
- err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -279,16 +230,16 @@ int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 data = S5K4AA_PAGE_MAP_2;
int err;
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
*val = (data & S5K4AA_RM_V_FLIP) >> 7;
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -298,35 +249,35 @@ int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
goto out;
data = ((data & ~S5K4AA_RM_V_FLIP)
| ((val & 0x01) << 7));
- err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
goto out;
if (val) {
- err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
if (err < 0)
goto out;
data++;
- err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
} else {
- err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
if (err < 0)
goto out;
data--;
- err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
}
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -335,15 +286,15 @@ int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 data = S5K4AA_PAGE_MAP_2;
int err;
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
*val = (data & S5K4AA_RM_H_FLIP) >> 6;
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -354,35 +305,35 @@ int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "Set horizontal flip to %d",
val);
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
goto out;
data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6));
- err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
goto out;
if (val) {
- err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
if (err < 0)
goto out;
data++;
- err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
if (err < 0)
goto out;
} else {
- err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
if (err < 0)
goto out;
data--;
- err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
}
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -391,16 +342,16 @@ int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 data = S5K4AA_PAGE_MAP_2;
int err;
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_read_sensor(sd, S5K4AA_GAIN_2, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_GAIN_2, &data, 1);
*val = data;
PDEBUG(D_V4L2, "Read gain %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -410,28 +361,28 @@ int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set gain to %d", val);
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
data = val & 0xff;
- err = s5k4aa_write_sensor(sd, S5K4AA_GAIN_2, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_GAIN_2, &data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
-void s5k4aa_dump_registers(struct sd *sd)
+static void s5k4aa_dump_registers(struct sd *sd)
{
int address;
u8 page, old_page;
- s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+ m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
for (page = 0; page < 16; page++) {
- s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
info("Dumping the s5k4aa register state for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 value = 0;
- s5k4aa_read_sensor(sd, address, &value, 1);
+ m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
@@ -439,15 +390,15 @@ void s5k4aa_dump_registers(struct sd *sd)
info("s5k4aa register state dump complete");
for (page = 0; page < 16; page++) {
- s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
info("Probing for which registers that are "
"read/write for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 old_value, ctrl_value, test_value = 0xff;
- s5k4aa_read_sensor(sd, address, &old_value, 1);
- s5k4aa_write_sensor(sd, address, &test_value, 1);
- s5k4aa_read_sensor(sd, address, &ctrl_value, 1);
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value)
info("register 0x%x is writeable", address);
@@ -455,9 +406,9 @@ void s5k4aa_dump_registers(struct sd *sd)
info("register 0x%x is read only", address);
/* Restore original value */
- s5k4aa_write_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &old_value, 1);
}
}
info("Read/write register probing complete");
- s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
}
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
index eaef67655afa..1f88b0d040c4 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
@@ -41,11 +41,10 @@
#define S5K4AA_WINDOW_HEIGHT_LO 0x09
#define S5K4AA_WINDOW_WIDTH_HI 0x0a
#define S5K4AA_WINDOW_WIDTH_LO 0x0b
-#define S5K4AA_GLOBAL_GAIN__ 0x0f /* Only a guess ATM !!! */
-#define S5K4AA_H_BLANK_HI__ 0x1d /* Only a guess ATM !!! sync lost
- if too low, reduces frame rate
- if too high */
-#define S5K4AA_H_BLANK_LO__ 0x1e /* Only a guess ATM !!! */
+#define S5K4AA_GLOBAL_GAIN__ 0x0f
+/* sync lost, if too low, reduces frame rate if too high */
+#define S5K4AA_H_BLANK_HI__ 0x1d
+#define S5K4AA_H_BLANK_LO__ 0x1e
#define S5K4AA_EXPOSURE_HI 0x17
#define S5K4AA_EXPOSURE_LO 0x18
#define S5K4AA_GAIN_1 0x1f /* (digital?) gain : 5 bits */
@@ -68,13 +67,6 @@ int s5k4aa_probe(struct sd *sd);
int s5k4aa_init(struct sd *sd);
int s5k4aa_power_down(struct sd *sd);
-void s5k4aa_dump_registers(struct sd *sd);
-
-int s5k4aa_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-int s5k4aa_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
@@ -89,9 +81,8 @@ static struct m5602_sensor s5k4aa = {
.probe = s5k4aa_probe,
.init = s5k4aa_init,
.power_down = s5k4aa_power_down,
- .read_sensor = s5k4aa_read_sensor,
- .write_sensor = s5k4aa_write_sensor,
.i2c_slave_id = 0x5a,
+ .i2c_regW = 2,
.nctrls = 4,
.ctrls = {
{
@@ -338,32 +329,4 @@ static const unsigned char init_s5k4aa[][4] =
{SENSOR, S5K4AA_GAIN_2, 0xa0, 0x00}
};
-static
- const
- struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
- {
- .ident = "Fujitsu-Siemens Amilo Xa 2528",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
- DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
- }
- },
- {
- .ident = "Fujitsu-Siemens Amilo Xi 2550",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
- DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
- }
- },
- {
- .ident = "MSI GX700",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
- DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
- DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
- }
- },
- { }
-};
-
#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
index 8988a728e0b4..af3f2dc2c702 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c
+++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
@@ -18,6 +18,8 @@
#include "m5602_s5k83a.h"
+static void s5k83a_dump_registers(struct sd *sd);
+
int s5k83a_probe(struct sd *sd)
{
u8 prod_id = 0, ver_id = 0;
@@ -39,7 +41,7 @@ int s5k83a_probe(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]};
if (preinit_s5k83a[i][0] == SENSOR)
- err = s5k83a_write_sensor(sd, preinit_s5k83a[i][1],
+ err = m5602_write_sensor(sd, preinit_s5k83a[i][1],
data, 2);
else
err = m5602_write_bridge(sd, preinit_s5k83a[i][1],
@@ -49,10 +51,10 @@ int s5k83a_probe(struct sd *sd)
/* We don't know what register (if any) that contain the product id
* Just pick the first addresses that seem to produce the same results
* on multiple machines */
- if (s5k83a_read_sensor(sd, 0x00, &prod_id, 1))
+ if (m5602_read_sensor(sd, 0x00, &prod_id, 1))
return -ENODEV;
- if (s5k83a_read_sensor(sd, 0x01, &ver_id, 1))
+ if (m5602_read_sensor(sd, 0x01, &ver_id, 1))
return -ENODEV;
if ((prod_id == 0xff) || (ver_id == 0xff))
@@ -68,91 +70,6 @@ sensor_found:
return 0;
}
-int s5k83a_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- sd->sensor->i2c_slave_id);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
- if (err < 0)
- goto out;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
-
- if (err < 0)
- goto out;
- for (i = 0; i < len && !len; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x containing 0x%x ", address, *i2c_data);
- }
-
-out:
- return (err < 0) ? err : 0;
-}
-
-int s5k83a_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* No sensor with a data width larger than 16 bits has yet been seen */
- if (len > 2 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton,
- sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- /* Special case larger sensor writes */
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the tailer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
-}
-
int s5k83a_init(struct sd *sd)
{
int i, err = 0;
@@ -169,14 +86,14 @@ int s5k83a_init(struct sd *sd)
case SENSOR:
data[0] = init_s5k83a[i][2];
- err = s5k83a_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_s5k83a[i][1], data, 1);
break;
case SENSOR_LONG:
data[0] = init_s5k83a[i][2];
data[1] = init_s5k83a[i][3];
- err = s5k83a_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_s5k83a[i][1], data, 2);
break;
default:
@@ -200,14 +117,14 @@ void s5k83a_dump_registers(struct sd *sd)
{
int address;
u8 page, old_page;
- s5k83a_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+ m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
for (page = 0; page < 16; page++) {
- s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+ m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
info("Dumping the s5k83a register state for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 val = 0;
- s5k83a_read_sensor(sd, address, &val, 1);
+ m5602_read_sensor(sd, address, &val, 1);
info("register 0x%x contains 0x%x",
address, val);
}
@@ -215,15 +132,15 @@ void s5k83a_dump_registers(struct sd *sd)
info("s5k83a register state dump complete");
for (page = 0; page < 16; page++) {
- s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+ m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
info("Probing for which registers that are read/write "
"for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 old_val, ctrl_val, test_val = 0xff;
- s5k83a_read_sensor(sd, address, &old_val, 1);
- s5k83a_write_sensor(sd, address, &test_val, 1);
- s5k83a_read_sensor(sd, address, &ctrl_val, 1);
+ m5602_read_sensor(sd, address, &old_val, 1);
+ m5602_write_sensor(sd, address, &test_val, 1);
+ m5602_read_sensor(sd, address, &ctrl_val, 1);
if (ctrl_val == test_val)
info("register 0x%x is writeable", address);
@@ -231,11 +148,11 @@ void s5k83a_dump_registers(struct sd *sd)
info("register 0x%x is read only", address);
/* Restore original val */
- s5k83a_write_sensor(sd, address, &old_val, 1);
+ m5602_write_sensor(sd, address, &old_val, 1);
}
}
info("Read/write register probing complete");
- s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+ m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
}
int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -244,11 +161,15 @@ int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev;
- err = s5k83a_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+ err = m5602_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+ if (err < 0)
+ goto out;
+
data[1] = data[1] << 1;
*val = data[1];
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -259,23 +180,24 @@ int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
data[0] = 0x00;
data[1] = 0x20;
- err = s5k83a_write_sensor(sd, 0x14, data, 2);
+ err = m5602_write_sensor(sd, 0x14, data, 2);
if (err < 0)
- return err;
+ goto out;
data[0] = 0x01;
data[1] = 0x00;
- err = s5k83a_write_sensor(sd, 0x0d, data, 2);
+ err = m5602_write_sensor(sd, 0x0d, data, 2);
if (err < 0)
- return err;
+ goto out;
/* FIXME: This is not sane, we need to figure out the composition
of these registers */
data[0] = val >> 3; /* brightness, high 5 bits */
data[1] = val >> 1; /* brightness, high 7 bits */
- err = s5k83a_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+ err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -284,10 +206,14 @@ int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val)
u8 data;
struct sd *sd = (struct sd *) gspca_dev;
- err = s5k83a_read_sensor(sd, S5K83A_WHITENESS, &data, 1);
+ err = m5602_read_sensor(sd, S5K83A_WHITENESS, &data, 1);
+ if (err < 0)
+ goto out;
*val = data;
- return (err < 0) ? err : 0;
+
+out:
+ return err;
}
int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val)
@@ -297,9 +223,9 @@ int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = val;
- err = s5k83a_write_sensor(sd, S5K83A_WHITENESS, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_WHITENESS, data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -308,7 +234,9 @@ int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev;
- err = s5k83a_read_sensor(sd, S5K83A_GAIN, data, 2);
+ err = m5602_read_sensor(sd, S5K83A_GAIN, data, 2);
+ if (err < 0)
+ goto out;
data[1] = data[1] & 0x3f;
if (data[1] > S5K83A_MAXIMUM_GAIN)
@@ -316,7 +244,8 @@ int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
*val = data[1];
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -327,9 +256,8 @@ int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
data[0] = 0;
data[1] = val;
- err = s5k83a_write_sensor(sd, S5K83A_GAIN, data, 2);
-
- return (err < 0) ? err : 0;
+ err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
+ return err;
}
int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -339,14 +267,15 @@ int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = 0x05;
- err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
- return err;
+ goto out;
- err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
*val = (data[0] | 0x40) ? 1 : 0;
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -356,25 +285,26 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = 0x05;
- err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
- return err;
+ goto out;
- err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0)
- return err;
+ goto out;
/* set or zero six bit, seven is hflip */
data[0] = (val) ? (data[0] & 0x80) | 0x40 | S5K83A_FLIP_MASK
: (data[0] & 0x80) | S5K83A_FLIP_MASK;
- err = s5k83a_write_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0)
- return err;
+ goto out;
data[0] = (val) ? 0x0b : 0x0a;
- err = s5k83a_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -384,14 +314,15 @@ int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = 0x05;
- err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
- return err;
+ goto out;
- err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
*val = (data[0] | 0x80) ? 1 : 0;
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -401,23 +332,23 @@ int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = 0x05;
- err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
- return err;
+ goto out;
- err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0)
- return err;
+ goto out;
/* set or zero seven bit, six is vflip */
data[0] = (val) ? (data[0] & 0x40) | 0x80 | S5K83A_FLIP_MASK
: (data[0] & 0x40) | S5K83A_FLIP_MASK;
- err = s5k83a_write_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0)
- return err;
+ goto out;
data[0] = (val) ? 0x0a : 0x0b;
- err = s5k83a_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
-
- return (err < 0) ? err : 0;
+ err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
+out:
+ return err;
}
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/drivers/media/video/gspca/m5602/m5602_s5k83a.h
index ee3ee9cfca1d..05ccb5b57a88 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k83a.h
+++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.h
@@ -22,15 +22,15 @@
#include "m5602_sensor.h"
#define S5K83A_FLIP 0x01
-#define S5K83A_HFLIP_TUNE 0x03
-#define S5K83A_VFLIP_TUNE 0x05
-#define S5K83A_WHITENESS 0x0a
+#define S5K83A_HFLIP_TUNE 0x03
+#define S5K83A_VFLIP_TUNE 0x05
+#define S5K83A_WHITENESS 0x0a
#define S5K83A_GAIN 0x18
-#define S5K83A_BRIGHTNESS 0x1b
-#define S5K83A_PAGE_MAP 0xec
+#define S5K83A_BRIGHTNESS 0x1b
+#define S5K83A_PAGE_MAP 0xec
-#define S5K83A_DEFAULT_BRIGHTNESS 0x71
-#define S5K83A_DEFAULT_WHITENESS 0x7e
+#define S5K83A_DEFAULT_BRIGHTNESS 0x71
+#define S5K83A_DEFAULT_WHITENESS 0x7e
#define S5K83A_DEFAULT_GAIN 0x00
#define S5K83A_MAXIMUM_GAIN 0x3c
#define S5K83A_FLIP_MASK 0x10
@@ -46,13 +46,6 @@ int s5k83a_probe(struct sd *sd);
int s5k83a_init(struct sd *sd);
int s5k83a_power_down(struct sd *sd);
-void s5k83a_dump_registers(struct sd *sd);
-
-int s5k83a_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-int s5k83a_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val);
@@ -64,15 +57,13 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
-
static struct m5602_sensor s5k83a = {
.name = "S5K83A",
.probe = s5k83a_probe,
.init = s5k83a_init,
.power_down = s5k83a_power_down,
- .read_sensor = s5k83a_read_sensor,
- .write_sensor = s5k83a_write_sensor,
.i2c_slave_id = 0x5a,
+ .i2c_regW = 2,
.nctrls = 5,
.ctrls = {
{
diff --git a/drivers/media/video/gspca/m5602/m5602_sensor.h b/drivers/media/video/gspca/m5602/m5602_sensor.h
index 60c9a48e0c02..261623f0da48 100644
--- a/drivers/media/video/gspca/m5602/m5602_sensor.h
+++ b/drivers/media/video/gspca/m5602/m5602_sensor.h
@@ -49,23 +49,21 @@ struct m5602_sensor {
/* What i2c address the sensor is connected to */
u8 i2c_slave_id;
+ /* Width of each i2c register (in bytes) */
+ u8 i2c_regW;
+
/* Probes if the sensor is connected */
int (*probe)(struct sd *sd);
/* Performs a initialization sequence */
int (*init)(struct sd *sd);
+ /* Executed when the camera starts to send data */
+ int (*start)(struct sd *sd);
+
/* Performs a power down sequence */
int (*power_down)(struct sd *sd);
- /* Reads a sensor register */
- int (*read_sensor)(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
- /* Writes to a sensor register */
- int (*write_sensor)(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
int nctrls;
struct ctrl ctrls[M5602_MAX_CTRLS];
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
index 277ca34a8817..3d2090e67a63 100644
--- a/drivers/media/video/gspca/mars.c
+++ b/drivers/media/video/gspca/mars.c
@@ -39,7 +39,7 @@ struct sd {
static struct ctrl sd_ctrls[] = {
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 589,
@@ -123,7 +123,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
cam->epaddr = 0x01;
cam->cam_mode = vga_mode;
- cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+ cam->nmodes = ARRAY_SIZE(vga_mode);
sd->qindex = 1; /* set the quantization table */
return 0;
}
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index ca671194679e..ee232956c812 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -3,7 +3,18 @@
*
* Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr)
*
- * (This module is adapted from the ov51x-jpeg package)
+ * This module is adapted from the ov51x-jpeg package, which itself
+ * was adapted from the ov511 driver.
+ *
+ * Original copyright for the ov511 driver is:
+ *
+ * Copyright (c) 1999-2004 Mark W. McClelland
+ * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach
+ *
+ * ov51x-jpeg original copyright is:
+ *
+ * Copyright (c) 2004-2007 Romain Beauxis <toots@rastageeks.org>
+ * Support for OV7670 sensors was contributed by Sam Skipsey <aoanla@yahoo.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
@@ -40,22 +51,18 @@ struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
/* Determined by sensor type */
- char sif;
-
- unsigned char primary_i2c_slave; /* I2C write id of sensor */
+ __u8 sif;
- unsigned char brightness;
- unsigned char contrast;
- unsigned char colors;
+ __u8 brightness;
+ __u8 contrast;
+ __u8 colors;
__u8 hflip;
__u8 vflip;
- char compress; /* Should the next frame be compressed? */
- char compress_inited; /* Are compression params uploaded? */
- char stopped; /* Streaming is temporarily paused */
+ __u8 stopped; /* Streaming is temporarily paused */
- char frame_rate; /* current Framerate (OV519 only) */
- char clockdiv; /* clockdiv override for OV519 only */
+ __u8 frame_rate; /* current Framerate (OV519 only) */
+ __u8 clockdiv; /* clockdiv override for OV519 only */
char sensor; /* Type of image sensor chip (SEN_*) */
#define SEN_UNKNOWN 0
@@ -67,7 +74,6 @@ struct sd {
#define SEN_OV7670 6
#define SEN_OV76BE 7
#define SEN_OV8610 8
-
};
/* V4L2 controls supported by the driver */
@@ -158,7 +164,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -170,7 +176,7 @@ static struct v4l2_pix_format vga_mode[] = {
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 0},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
@@ -184,15 +190,15 @@ static struct v4l2_pix_format sif_mode[] = {
};
/* OV519 Camera interface register numbers */
-#define OV519_CAM_H_SIZE 0x10
-#define OV519_CAM_V_SIZE 0x11
-#define OV519_CAM_X_OFFSETL 0x12
-#define OV519_CAM_X_OFFSETH 0x13
-#define OV519_CAM_Y_OFFSETL 0x14
-#define OV519_CAM_Y_OFFSETH 0x15
-#define OV519_CAM_DIVIDER 0x16
-#define OV519_CAM_DFR 0x20
-#define OV519_CAM_FORMAT 0x25
+#define OV519_R10_H_SIZE 0x10
+#define OV519_R11_V_SIZE 0x11
+#define OV519_R12_X_OFFSETL 0x12
+#define OV519_R13_X_OFFSETH 0x13
+#define OV519_R14_Y_OFFSETL 0x14
+#define OV519_R15_Y_OFFSETH 0x15
+#define OV519_R16_DIVIDER 0x16
+#define OV519_R20_DFR 0x20
+#define OV519_R25_FORMAT 0x25
/* OV519 System Controller register numbers */
#define OV519_SYS_RESET1 0x51
@@ -562,8 +568,8 @@ static const struct ov_i2c_regvals norm_7670[] = {
{ OV7670_REG_VSTOP, 0x7a },
{ OV7670_REG_VREF, 0x0a },
- { OV7670_REG_COM3, 0 },
- { OV7670_REG_COM14, 0 },
+ { OV7670_REG_COM3, 0x00 },
+ { OV7670_REG_COM14, 0x00 },
/* Mystery scaling numbers */
{ 0x70, 0x3a },
{ 0x71, 0x35 },
@@ -595,8 +601,8 @@ static const struct ov_i2c_regvals norm_7670[] = {
{ OV7670_REG_COM8, OV7670_COM8_FASTAEC
| OV7670_COM8_AECSTEP
| OV7670_COM8_BFILT },
- { OV7670_REG_GAIN, 0 },
- { OV7670_REG_AECH, 0 },
+ { OV7670_REG_GAIN, 0x00 },
+ { OV7670_REG_AECH, 0x00 },
{ OV7670_REG_COM4, 0x40 }, /* magic reserved bit */
{ OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
{ OV7670_REG_BD50MAX, 0x05 },
@@ -634,16 +640,16 @@ static const struct ov_i2c_regvals norm_7670[] = {
{ OV7670_REG_COM12, 0x78 },
{ 0x4d, 0x40 },
{ 0x4e, 0x20 },
- { OV7670_REG_GFIX, 0 },
+ { OV7670_REG_GFIX, 0x00 },
{ 0x6b, 0x4a },
{ 0x74, 0x10 },
{ 0x8d, 0x4f },
- { 0x8e, 0 },
- { 0x8f, 0 },
- { 0x90, 0 },
- { 0x91, 0 },
- { 0x96, 0 },
- { 0x9a, 0 },
+ { 0x8e, 0x00 },
+ { 0x8f, 0x00 },
+ { 0x90, 0x00 },
+ { 0x91, 0x00 },
+ { 0x96, 0x00 },
+ { 0x9a, 0x00 },
{ 0xb0, 0x84 },
{ 0xb1, 0x0c },
{ 0xb2, 0x0e },
@@ -681,17 +687,17 @@ static const struct ov_i2c_regvals norm_7670[] = {
/* Matrix coefficients */
{ 0x4f, 0x80 },
{ 0x50, 0x80 },
- { 0x51, 0 },
+ { 0x51, 0x00 },
{ 0x52, 0x22 },
{ 0x53, 0x5e },
{ 0x54, 0x80 },
{ 0x58, 0x9e },
{ OV7670_REG_COM16, OV7670_COM16_AWBGAIN },
- { OV7670_REG_EDGE, 0 },
+ { OV7670_REG_EDGE, 0x00 },
{ 0x75, 0x05 },
{ 0x76, 0xe1 },
- { 0x4c, 0 },
+ { 0x4c, 0x00 },
{ 0x77, 0x01 },
{ OV7670_REG_COM13, OV7670_COM13_GAMMA
| OV7670_COM13_UVSAT
@@ -704,7 +710,7 @@ static const struct ov_i2c_regvals norm_7670[] = {
{ 0x34, 0x11 },
{ OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO },
{ 0xa4, 0x88 },
- { 0x96, 0 },
+ { 0x96, 0x00 },
{ 0x97, 0x30 },
{ 0x98, 0x20 },
{ 0x99, 0x30 },
@@ -942,11 +948,11 @@ static int i2c_w(struct sd *sd,
/* Initiate 3-byte write cycle */
rc = reg_w(sd, R518_I2C_CTL, 0x01);
+ if (rc < 0)
+ return rc;
/* wait for write complete */
msleep(4);
- if (rc < 0)
- return rc;
return reg_r8(sd, R518_I2C_CTL);
}
@@ -1029,7 +1035,7 @@ static inline int ov51x_restart(struct sd *sd)
*/
static int init_ov_sensor(struct sd *sd)
{
- int i, success;
+ int i;
/* Reset the sensor */
if (i2c_w(sd, 0x12, 0x80) < 0)
@@ -1038,11 +1044,11 @@ static int init_ov_sensor(struct sd *sd)
/* Wait for it to initialize */
msleep(150);
- for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
+ for (i = 0; i < i2c_detect_tries; i++) {
if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f &&
i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) {
- success = 1;
- continue;
+ PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i);
+ return 0;
}
/* Reset the sensor */
@@ -1054,10 +1060,7 @@ static int init_ov_sensor(struct sd *sd)
if (i2c_r(sd, 0x00) < 0)
return -EIO;
}
- if (!success)
- return -EIO;
- PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i);
- return 0;
+ return -EIO;
}
/* Set the read and write slave IDs. The "slave" argument is the write slave,
@@ -1073,7 +1076,6 @@ static int ov51x_set_slave_ids(struct sd *sd,
rc = reg_w(sd, R51x_I2C_W_SID, slave);
if (rc < 0)
return rc;
- sd->primary_i2c_slave = slave;
return reg_w(sd, R51x_I2C_R_SID, slave + 1);
}
@@ -1285,7 +1287,6 @@ static int ov6xx0_configure(struct sd *sd)
/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */
static void ov51x_led_control(struct sd *sd, int on)
{
-/* PDEBUG(D_STREAM, "LED (%s)", on ? "on" : "off"); */
reg_w_mask(sd, OV519_GPIO_DATA_OUT0, !on, 1); /* 0 / 1 */
}
@@ -1352,7 +1353,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
}
if (ov8xx0_configure(sd) < 0) {
PDEBUG(D_ERR,
- "Failed to configure OV8xx0 sensor");
+ "Failed to configure OV8xx0 sensor");
goto error;
}
}
@@ -1482,7 +1483,7 @@ static int ov519_mode_init_regs(struct sd *sd)
return -EIO;
if (sd->sensor == SEN_OV7640) {
/* Select 8-bit input mode */
- reg_w_mask(sd, OV519_CAM_DFR, 0x10, 0x10);
+ reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10);
}
} else {
if (write_regvals(sd, mode_init_519_ov7670,
@@ -1490,14 +1491,14 @@ static int ov519_mode_init_regs(struct sd *sd)
return -EIO;
}
- reg_w(sd, OV519_CAM_H_SIZE, sd->gspca_dev.width >> 4);
- reg_w(sd, OV519_CAM_V_SIZE, sd->gspca_dev.height >> 3);
- reg_w(sd, OV519_CAM_X_OFFSETL, 0x00);
- reg_w(sd, OV519_CAM_X_OFFSETH, 0x00);
- reg_w(sd, OV519_CAM_Y_OFFSETL, 0x00);
- reg_w(sd, OV519_CAM_Y_OFFSETH, 0x00);
- reg_w(sd, OV519_CAM_DIVIDER, 0x00);
- reg_w(sd, OV519_CAM_FORMAT, 0x03); /* YUV422 */
+ reg_w(sd, OV519_R10_H_SIZE, sd->gspca_dev.width >> 4);
+ reg_w(sd, OV519_R11_V_SIZE, sd->gspca_dev.height >> 3);
+ reg_w(sd, OV519_R12_X_OFFSETL, 0x00);
+ reg_w(sd, OV519_R13_X_OFFSETH, 0x00);
+ reg_w(sd, OV519_R14_Y_OFFSETL, 0x00);
+ reg_w(sd, OV519_R15_Y_OFFSETH, 0x00);
+ reg_w(sd, OV519_R16_DIVIDER, 0x00);
+ reg_w(sd, OV519_R25_FORMAT, 0x03); /* YUV422 */
reg_w(sd, 0x26, 0x00); /* Undocumented */
/******** Set the framerate ********/
@@ -1509,8 +1510,8 @@ static int ov519_mode_init_regs(struct sd *sd)
switch (sd->sensor) {
case SEN_OV7640:
switch (sd->frame_rate) {
-/*fixme: default was 30 fps */
- case 30:
+ default:
+/* case 30: */
reg_w(sd, 0xa4, 0x0c);
reg_w(sd, 0x23, 0xff);
break;
@@ -1522,8 +1523,7 @@ static int ov519_mode_init_regs(struct sd *sd)
reg_w(sd, 0xa4, 0x0c);
reg_w(sd, 0x23, 0x1b);
break;
- default:
-/* case 15: */
+ case 15:
reg_w(sd, 0xa4, 0x04);
reg_w(sd, 0x23, 0xff);
sd->clockdiv = 1;
@@ -1576,7 +1576,6 @@ static int ov519_mode_init_regs(struct sd *sd)
}
break;
}
-
return 0;
}
@@ -1667,7 +1666,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd)
* the gain or the contrast. The "reserved" bits seem
* to have some effect in this case. */
i2c_w(sd, 0x2d, 0x85);
- } else if (sd->clockdiv >= 0) {
+ } else {
i2c_w(sd, 0x11, sd->clockdiv);
}
@@ -1869,7 +1868,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
ret = ov51x_restart(sd);
if (ret < 0)
goto out;
- PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt);
ov51x_led_control(sd, 1);
return 0;
out:
@@ -1879,8 +1877,10 @@ out:
static void sd_stopN(struct gspca_dev *gspca_dev)
{
- ov51x_stop((struct sd *) gspca_dev);
- ov51x_led_control((struct sd *) gspca_dev, 0);
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ ov51x_stop(sd);
+ ov51x_led_control(sd, 0);
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
@@ -1935,9 +1935,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
int val;
val = sd->brightness;
- PDEBUG(D_CONF, "brightness:%d", val);
-/* if (gspca_dev->streaming)
- * ov51x_stop(sd); */
switch (sd->sensor) {
case SEN_OV8610:
case SEN_OV7610:
@@ -1959,8 +1956,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
i2c_w(sd, OV7670_REG_BRIGHT, ov7670_abs_to_sm(val));
break;
}
-/* if (gspca_dev->streaming)
- * ov51x_restart(sd); */
}
static void setcontrast(struct gspca_dev *gspca_dev)
@@ -1969,9 +1964,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
int val;
val = sd->contrast;
- PDEBUG(D_CONF, "contrast:%d", val);
-/* if (gspca_dev->streaming)
- ov51x_stop(sd); */
switch (sd->sensor) {
case SEN_OV7610:
case SEN_OV6620:
@@ -2007,8 +1999,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
i2c_w(sd, OV7670_REG_CONTRAS, val >> 1);
break;
}
-/* if (gspca_dev->streaming)
- ov51x_restart(sd); */
}
static void setcolors(struct gspca_dev *gspca_dev)
@@ -2017,9 +2007,6 @@ static void setcolors(struct gspca_dev *gspca_dev)
int val;
val = sd->colors;
- PDEBUG(D_CONF, "saturation:%d", val);
-/* if (gspca_dev->streaming)
- ov51x_stop(sd); */
switch (sd->sensor) {
case SEN_OV8610:
case SEN_OV7610:
@@ -2044,8 +2031,6 @@ static void setcolors(struct gspca_dev *gspca_dev)
/* set REG_COM13 values for UV sat auto mode */
break;
}
-/* if (gspca_dev->streaming)
- ov51x_restart(sd); */
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -2053,7 +2038,8 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->brightness = val;
- setbrightness(gspca_dev);
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
return 0;
}
@@ -2070,7 +2056,8 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->contrast = val;
- setcontrast(gspca_dev);
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
return 0;
}
@@ -2087,7 +2074,8 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->colors = val;
- setcolors(gspca_dev);
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
return 0;
}
@@ -2104,7 +2092,8 @@ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->hflip = val;
- sethvflip(sd);
+ if (gspca_dev->streaming)
+ sethvflip(sd);
return 0;
}
@@ -2121,7 +2110,8 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->vflip = val;
- sethvflip(sd);
+ if (gspca_dev->streaming)
+ sethvflip(sd);
return 0;
}
@@ -2162,7 +2152,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x05a9, 0x8519)},
{}
};
-#undef DVNAME
+
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
new file mode 100644
index 000000000000..3bf15e401693
--- /dev/null
+++ b/drivers/media/video/gspca/ov534.c
@@ -0,0 +1,601 @@
+/*
+ * ov534/ov772x gspca driver
+ * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
+ * Copyright (C) 2008 Jim Paris <jim@jtan.com>
+ *
+ * Based on a prototype written by Mark Ferrell <majortrips@gmail.com>
+ * USB protocol reverse engineered by Jim Paris <jim@jtan.com>
+ * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/
+ *
+ * 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
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define MODULE_NAME "ov534"
+
+#include "gspca.h"
+
+#define OV534_REG_ADDRESS 0xf1 /* ? */
+#define OV534_REG_SUBADDR 0xf2
+#define OV534_REG_WRITE 0xf3
+#define OV534_REG_READ 0xf4
+#define OV534_REG_OPERATION 0xf5
+#define OV534_REG_STATUS 0xf6
+
+#define OV534_OP_WRITE_3 0x37
+#define OV534_OP_WRITE_2 0x33
+#define OV534_OP_READ_2 0xf9
+
+#define CTRL_TIMEOUT 500
+
+MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
+MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ __u32 last_fid;
+ __u32 last_pts;
+ int frame_rate;
+};
+
+/* V4L2 controls supported by the driver */
+static struct ctrl sd_ctrls[] = {
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+ .bytesperline = 640 * 2,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int ret;
+
+ PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val);
+ gspca_dev->usb_buf[0] = val;
+ ret = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ 0x1,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+ if (ret < 0)
+ PDEBUG(D_ERR, "write failed");
+}
+
+static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int ret;
+
+ ret = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ 0x1,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+ PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]);
+ if (ret < 0)
+ PDEBUG(D_ERR, "read failed");
+ return gspca_dev->usb_buf[0];
+}
+
+/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
+ * (direction and output)? */
+static void ov534_set_led(struct gspca_dev *gspca_dev, int status)
+{
+ u8 data;
+
+ PDEBUG(D_CONF, "led status: %d", status);
+
+ data = ov534_reg_read(gspca_dev, 0x21);
+ data |= 0x80;
+ ov534_reg_write(gspca_dev, 0x21, data);
+
+ data = ov534_reg_read(gspca_dev, 0x23);
+ if (status)
+ data |= 0x80;
+ else
+ data &= ~(0x80);
+
+ ov534_reg_write(gspca_dev, 0x23, data);
+}
+
+static int sccb_check_status(struct gspca_dev *gspca_dev)
+{
+ u8 data;
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ data = ov534_reg_read(gspca_dev, OV534_REG_STATUS);
+
+ switch (data) {
+ case 0x00:
+ return 1;
+ case 0x04:
+ return 0;
+ case 0x03:
+ break;
+ default:
+ PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5",
+ data, i + 1);
+ }
+ }
+ return 0;
+}
+
+static void sccb_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+{
+ PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%02x", reg, val);
+ ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
+ ov534_reg_write(gspca_dev, OV534_REG_WRITE, val);
+ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
+
+ if (!sccb_check_status(gspca_dev))
+ PDEBUG(D_ERR, "sccb_reg_write failed");
+}
+
+#ifdef GSPCA_DEBUG
+static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+ ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
+ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
+ if (!sccb_check_status(gspca_dev))
+ PDEBUG(D_ERR, "sccb_reg_read failed 1");
+
+ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
+ if (!sccb_check_status(gspca_dev))
+ PDEBUG(D_ERR, "sccb_reg_read failed 2");
+
+ return ov534_reg_read(gspca_dev, OV534_REG_READ);
+}
+#endif
+
+static const __u8 ov534_reg_initdata[][2] = {
+ { 0xe7, 0x3a },
+
+ { OV534_REG_ADDRESS, 0x42 }, /* select OV772x sensor */
+
+ { 0xc2, 0x0c },
+ { 0x88, 0xf8 },
+ { 0xc3, 0x69 },
+ { 0x89, 0xff },
+ { 0x76, 0x03 },
+ { 0x92, 0x01 },
+ { 0x93, 0x18 },
+ { 0x94, 0x10 },
+ { 0x95, 0x10 },
+ { 0xe2, 0x00 },
+ { 0xe7, 0x3e },
+
+ { 0x96, 0x00 },
+
+ { 0x97, 0x20 },
+ { 0x97, 0x20 },
+ { 0x97, 0x20 },
+ { 0x97, 0x0a },
+ { 0x97, 0x3f },
+ { 0x97, 0x4a },
+ { 0x97, 0x20 },
+ { 0x97, 0x15 },
+ { 0x97, 0x0b },
+
+ { 0x8e, 0x40 },
+ { 0x1f, 0x81 },
+ { 0x34, 0x05 },
+ { 0xe3, 0x04 },
+ { 0x88, 0x00 },
+ { 0x89, 0x00 },
+ { 0x76, 0x00 },
+ { 0xe7, 0x2e },
+ { 0x31, 0xf9 },
+ { 0x25, 0x42 },
+ { 0x21, 0xf0 },
+
+ { 0x1c, 0x00 },
+ { 0x1d, 0x40 },
+ { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */
+ { 0x1d, 0x00 }, /* payload size */
+ { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */
+ { 0x1d, 0x58 }, /* frame size */
+ { 0x1d, 0x00 }, /* frame size */
+
+ { 0x1c, 0x0a },
+ { 0x1d, 0x08 }, /* turn on UVC header */
+ { 0x1d, 0x0e }, /* .. */
+
+ { 0x8d, 0x1c },
+ { 0x8e, 0x80 },
+ { 0xe5, 0x04 },
+
+ { 0xc0, 0x50 },
+ { 0xc1, 0x3c },
+ { 0xc2, 0x0c },
+};
+
+static const __u8 ov772x_reg_initdata[][2] = {
+ { 0x12, 0x80 },
+ { 0x11, 0x01 },
+
+ { 0x3d, 0x03 },
+ { 0x17, 0x26 },
+ { 0x18, 0xa0 },
+ { 0x19, 0x07 },
+ { 0x1a, 0xf0 },
+ { 0x32, 0x00 },
+ { 0x29, 0xa0 },
+ { 0x2c, 0xf0 },
+ { 0x65, 0x20 },
+ { 0x11, 0x01 },
+ { 0x42, 0x7f },
+ { 0x63, 0xe0 },
+ { 0x64, 0xff },
+ { 0x66, 0x00 },
+ { 0x13, 0xf0 },
+ { 0x0d, 0x41 },
+ { 0x0f, 0xc5 },
+ { 0x14, 0x11 },
+
+ { 0x22, 0x7f },
+ { 0x23, 0x03 },
+ { 0x24, 0x40 },
+ { 0x25, 0x30 },
+ { 0x26, 0xa1 },
+ { 0x2a, 0x00 },
+ { 0x2b, 0x00 },
+ { 0x6b, 0xaa },
+ { 0x13, 0xff },
+
+ { 0x90, 0x05 },
+ { 0x91, 0x01 },
+ { 0x92, 0x03 },
+ { 0x93, 0x00 },
+ { 0x94, 0x60 },
+ { 0x95, 0x3c },
+ { 0x96, 0x24 },
+ { 0x97, 0x1e },
+ { 0x98, 0x62 },
+ { 0x99, 0x80 },
+ { 0x9a, 0x1e },
+ { 0x9b, 0x08 },
+ { 0x9c, 0x20 },
+ { 0x9e, 0x81 },
+
+ { 0xa6, 0x04 },
+ { 0x7e, 0x0c },
+ { 0x7f, 0x16 },
+ { 0x80, 0x2a },
+ { 0x81, 0x4e },
+ { 0x82, 0x61 },
+ { 0x83, 0x6f },
+ { 0x84, 0x7b },
+ { 0x85, 0x86 },
+ { 0x86, 0x8e },
+ { 0x87, 0x97 },
+ { 0x88, 0xa4 },
+ { 0x89, 0xaf },
+ { 0x8a, 0xc5 },
+ { 0x8b, 0xd7 },
+ { 0x8c, 0xe8 },
+ { 0x8d, 0x20 },
+
+ { 0x0c, 0x90 },
+
+ { 0x2b, 0x00 },
+ { 0x22, 0x7f },
+ { 0x23, 0x03 },
+ { 0x11, 0x01 },
+ { 0x0c, 0xd0 },
+ { 0x64, 0xff },
+ { 0x0d, 0x41 },
+
+ { 0x14, 0x41 },
+ { 0x0e, 0xcd },
+ { 0xac, 0xbf },
+ { 0x8e, 0x00 },
+ { 0x0c, 0xd0 }
+};
+
+/* set framerate */
+static void ov534_set_frame_rate(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int fr = sd->frame_rate;
+
+ switch (fr) {
+ case 50:
+ sccb_reg_write(gspca_dev, 0x11, 0x01);
+ sccb_reg_write(gspca_dev, 0x0d, 0x41);
+ ov534_reg_write(gspca_dev, 0xe5, 0x02);
+ break;
+ case 40:
+ sccb_reg_write(gspca_dev, 0x11, 0x02);
+ sccb_reg_write(gspca_dev, 0x0d, 0xc1);
+ ov534_reg_write(gspca_dev, 0xe5, 0x04);
+ break;
+/* case 30: */
+ default:
+ fr = 30;
+ sccb_reg_write(gspca_dev, 0x11, 0x04);
+ sccb_reg_write(gspca_dev, 0x0d, 0x81);
+ ov534_reg_write(gspca_dev, 0xe5, 0x02);
+ break;
+ case 15:
+ sccb_reg_write(gspca_dev, 0x11, 0x03);
+ sccb_reg_write(gspca_dev, 0x0d, 0x41);
+ ov534_reg_write(gspca_dev, 0xe5, 0x04);
+ break;
+ }
+
+ sd->frame_rate = fr;
+ PDEBUG(D_PROBE, "frame_rate: %d", fr);
+}
+
+/* setup method */
+static void ov534_setup(struct gspca_dev *gspca_dev)
+{
+ int i;
+
+ /* Initialize bridge chip */
+ for (i = 0; i < ARRAY_SIZE(ov534_reg_initdata); i++)
+ ov534_reg_write(gspca_dev, ov534_reg_initdata[i][0],
+ ov534_reg_initdata[i][1]);
+
+ PDEBUG(D_PROBE, "sensor is ov%02x%02x",
+ sccb_reg_read(gspca_dev, 0x0a),
+ sccb_reg_read(gspca_dev, 0x0b));
+
+ ov534_set_led(gspca_dev, 1);
+
+ /* Initialize sensor */
+ for (i = 0; i < ARRAY_SIZE(ov772x_reg_initdata); i++)
+ sccb_reg_write(gspca_dev, ov772x_reg_initdata[i][0],
+ ov772x_reg_initdata[i][1]);
+
+ ov534_reg_write(gspca_dev, 0xe0, 0x09);
+ ov534_set_led(gspca_dev, 0);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+
+ cam->epaddr = 0x01;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+ cam->bulk_size = 16384;
+ cam->bulk_nurbs = 2;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ ov534_setup(gspca_dev);
+ ov534_set_frame_rate(gspca_dev);
+
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ /* start streaming data */
+ ov534_set_led(gspca_dev, 1);
+ ov534_reg_write(gspca_dev, 0xe0, 0x00);
+
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ /* stop streaming data */
+ ov534_reg_write(gspca_dev, 0xe0, 0x09);
+ ov534_set_led(gspca_dev, 0);
+}
+
+/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+#define UVC_STREAM_EOH (1 << 7)
+#define UVC_STREAM_ERR (1 << 6)
+#define UVC_STREAM_STI (1 << 5)
+#define UVC_STREAM_RES (1 << 4)
+#define UVC_STREAM_SCR (1 << 3)
+#define UVC_STREAM_PTS (1 << 2)
+#define UVC_STREAM_EOF (1 << 1)
+#define UVC_STREAM_FID (1 << 0)
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
+ __u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u32 this_pts;
+ int this_fid;
+ int remaining_len = len;
+ __u8 *next_data = data;
+
+scan_next:
+ if (remaining_len <= 0)
+ return;
+
+ data = next_data;
+ len = min(remaining_len, 2048);
+ remaining_len -= len;
+ next_data += len;
+
+ /* Payloads are prefixed with a UVC-style header. We
+ consider a frame to start when the FID toggles, or the PTS
+ changes. A frame ends when EOF is set, and we've received
+ the correct number of bytes. */
+
+ /* Verify UVC header. Header length is always 12 */
+ if (data[0] != 12 || len < 12) {
+ PDEBUG(D_PACK, "bad header");
+ goto discard;
+ }
+
+ /* Check errors */
+ if (data[1] & UVC_STREAM_ERR) {
+ PDEBUG(D_PACK, "payload error");
+ goto discard;
+ }
+
+ /* Extract PTS and FID */
+ if (!(data[1] & UVC_STREAM_PTS)) {
+ PDEBUG(D_PACK, "PTS not present");
+ goto discard;
+ }
+ this_pts = (data[5] << 24) | (data[4] << 16) | (data[3] << 8) | data[2];
+ this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0;
+
+ /* If PTS or FID has changed, start a new frame. */
+ if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
+ gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0);
+ sd->last_pts = this_pts;
+ sd->last_fid = this_fid;
+ }
+
+ /* Add the data from this payload */
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ data + 12, len - 12);
+
+ /* If this packet is marked as EOF, end the frame */
+ if (data[1] & UVC_STREAM_EOF) {
+ sd->last_pts = 0;
+
+ if ((frame->data_end - frame->data) !=
+ (gspca_dev->width * gspca_dev->height * 2)) {
+ PDEBUG(D_PACK, "short frame");
+ goto discard;
+ }
+
+ gspca_frame_add(gspca_dev, LAST_PACKET, frame, NULL, 0);
+ }
+
+ /* Done this payload */
+ goto scan_next;
+
+discard:
+ /* Discard data until a new frame starts. */
+ gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0);
+ goto scan_next;
+}
+
+/* get stream parameters (framerate) */
+static int sd_get_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cp->capability |= V4L2_CAP_TIMEPERFRAME;
+ tpf->numerator = 1;
+ tpf->denominator = sd->frame_rate;
+
+ return 0;
+}
+
+/* set stream parameters (framerate) */
+static int sd_set_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* Set requested framerate */
+ sd->frame_rate = tpf->denominator / tpf->numerator;
+ ov534_set_frame_rate(gspca_dev);
+
+ /* Return the actual framerate */
+ tpf->numerator = 1;
+ tpf->denominator = sd->frame_rate;
+
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .get_streamparm = sd_get_streamparm,
+ .set_streamparm = sd_set_streamparm,
+};
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x06f8, 0x3002)}, /* Hercules Blog Webcam */
+ {USB_DEVICE(0x06f8, 0x3003)}, /* Hercules Dualpix HD Weblog */
+ {USB_DEVICE(0x1415, 0x2000)}, /* Sony HD Eye for PS3 (SLEH 00201) */
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ if (usb_register(&sd_driver) < 0)
+ return -1;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+}
+
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
index 0b0c573d06da..c90ac852bac0 100644
--- a/drivers/media/video/gspca/pac207.c
+++ b/drivers/media/video/gspca/pac207.c
@@ -1,7 +1,7 @@
/*
* Pixart PAC207BCA library
*
- * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+ * Copyright (C) 2008 Hans de Goede <hdgoede@redhat.com>
* Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
* Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
*
@@ -27,7 +27,7 @@
#include "gspca.h"
-MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
+MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
MODULE_DESCRIPTION("Pixart PAC207");
MODULE_LICENSE("GPL");
@@ -149,7 +149,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = (176 + 2) * 144,
@@ -529,6 +529,7 @@ static const struct sd_desc sd_desc = {
static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x041e, 0x4028)},
{USB_DEVICE(0x093a, 0x2460)},
+ {USB_DEVICE(0x093a, 0x2461)},
{USB_DEVICE(0x093a, 0x2463)},
{USB_DEVICE(0x093a, 0x2464)},
{USB_DEVICE(0x093a, 0x2468)},
@@ -536,6 +537,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x093a, 0x2471)},
{USB_DEVICE(0x093a, 0x2472)},
{USB_DEVICE(0x093a, 0x2476)},
+ {USB_DEVICE(0x145f, 0x013a)},
{USB_DEVICE(0x2001, 0xf115)},
{}
};
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
index fbd45e235d97..a9c95cba710e 100644
--- a/drivers/media/video/gspca/pac7311.c
+++ b/drivers/media/video/gspca/pac7311.c
@@ -226,7 +226,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 8 + 590,
@@ -1064,10 +1064,13 @@ static __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311},
{USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311},
{USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311},
+ {USB_DEVICE(0x093a, 0x2620), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302},
+ {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302},
+ {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302},
{}
};
MODULE_DEVICE_TABLE(usb, device_table);
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
index 6c69bc7778fc..b3e4e0677b68 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/video/gspca/sonixb.c
@@ -132,8 +132,6 @@ struct sensor_data {
ignore atleast the 2 next frames for the new settings to come into effect
before doing any other adjustments */
#define AUTOGAIN_IGNORE_FRAMES 3
-#define AUTOGAIN_DEADZONE 1000
-#define DESIRED_AVG_LUM 7000
/* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
@@ -229,7 +227,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120,
@@ -251,7 +249,7 @@ static struct v4l2_pix_format vga_mode[] = {
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120,
@@ -827,18 +825,29 @@ static void setfreq(struct gspca_dev *gspca_dev)
static void do_autogain(struct gspca_dev *gspca_dev)
{
+ int deadzone, desired_avg_lum;
struct sd *sd = (struct sd *) gspca_dev;
int avg_lum = atomic_read(&sd->avg_lum);
if (avg_lum == -1)
return;
+ /* SIF / VGA sensors have a different autoexposure area and thus
+ different avg_lum values for the same picture brightness */
+ if (sensor_data[sd->sensor].flags & F_SIF) {
+ deadzone = 1000;
+ desired_avg_lum = 7000;
+ } else {
+ deadzone = 3000;
+ desired_avg_lum = 23000;
+ }
+
if (sd->autogain_ignore_frames > 0)
sd->autogain_ignore_frames--;
else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
- sd->brightness * DESIRED_AVG_LUM / 127,
- AUTOGAIN_DEADZONE, GAIN_KNEE, EXPOSURE_KNEE)) {
- PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d\n",
+ sd->brightness * desired_avg_lum / 127,
+ deadzone, GAIN_KNEE, EXPOSURE_KNEE)) {
+ PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d",
(int)sd->gain, (int)sd->exposure);
sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
}
@@ -1226,8 +1235,8 @@ static __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)},
{USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)},
{USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)},
- {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)},
#endif
+ {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)},
{USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)},
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
{USB_DEVICE(0x0c45, 0x602e), SB(OV7630, 102)},
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index 53cb82d9e7c6..3373b8d9d2a8 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -24,6 +24,8 @@
#include "gspca.h"
#include "jpeg.h"
+#define V4L2_CID_INFRARED (V4L2_CID_PRIVATE_BASE + 0)
+
MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver");
MODULE_LICENSE("GPL");
@@ -35,23 +37,26 @@ struct sd {
atomic_t avg_lum;
unsigned int exposure;
- unsigned short brightness;
- unsigned char contrast;
- unsigned char colors;
- unsigned char autogain;
+ __u16 brightness;
+ __u8 contrast;
+ __u8 colors;
+ __u8 autogain;
+ __u8 blue;
+ __u8 red;
__u8 vflip; /* ov7630 only */
+ __u8 infrared; /* mi0360 only */
- signed char ag_cnt;
+ __s8 ag_cnt;
#define AG_CNT_START 13
- char qindex;
- unsigned char bridge;
+ __u8 qindex;
+ __u8 bridge;
#define BRIDGE_SN9C102P 0
#define BRIDGE_SN9C105 1
#define BRIDGE_SN9C110 2
#define BRIDGE_SN9C120 3
#define BRIDGE_SN9C325 4
- char sensor; /* Type of image sensor chip */
+ __u8 sensor; /* Type of image sensor chip */
#define SENSOR_HV7131R 0
#define SENSOR_MI0360 1
#define SENSOR_MO4000 2
@@ -59,7 +64,7 @@ struct sd {
#define SENSOR_OV7630 4
#define SENSOR_OV7648 5
#define SENSOR_OV7660 6
- unsigned char i2c_base;
+ __u8 i2c_base;
};
/* V4L2 controls supported by the driver */
@@ -69,10 +74,16 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setinfrared(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
{
@@ -84,7 +95,7 @@ static struct ctrl sd_ctrls[] = {
#define BRIGHTNESS_MAX 0xffff
.maximum = BRIGHTNESS_MAX,
.step = 1,
-#define BRIGHTNESS_DEF 0x7fff
+#define BRIGHTNESS_DEF 0x8000
.default_value = BRIGHTNESS_DEF,
},
.set = sd_setbrightness,
@@ -111,7 +122,7 @@ static struct ctrl sd_ctrls[] = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Color",
.minimum = 0,
- .maximum = 64,
+ .maximum = 40,
.step = 1,
#define COLOR_DEF 32
.default_value = COLOR_DEF,
@@ -119,7 +130,35 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setcolors,
.get = sd_getcolors,
},
-#define AUTOGAIN_IDX 3
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 24,
+ .maximum = 40,
+ .step = 1,
+#define BLUE_BALANCE_DEF 32
+ .default_value = BLUE_BALANCE_DEF,
+ },
+ .set = sd_setblue_balance,
+ .get = sd_getblue_balance,
+ },
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 24,
+ .maximum = 40,
+ .step = 1,
+#define RED_BALANCE_DEF 32
+ .default_value = RED_BALANCE_DEF,
+ },
+ .set = sd_setred_balance,
+ .get = sd_getred_balance,
+ },
+#define AUTOGAIN_IDX 5
{
{
.id = V4L2_CID_AUTOGAIN,
@@ -135,7 +174,7 @@ static struct ctrl sd_ctrls[] = {
.get = sd_getautogain,
},
/* ov7630 only */
-#define VFLIP_IDX 4
+#define VFLIP_IDX 6
{
{
.id = V4L2_CID_VFLIP,
@@ -150,9 +189,43 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setvflip,
.get = sd_getvflip,
},
+/* mi0360 only */
+#define INFRARED_IDX 7
+ {
+ {
+ .id = V4L2_CID_INFRARED,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Infrared",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define INFRARED_DEF 0
+ .default_value = INFRARED_DEF,
+ },
+ .set = sd_setinfrared,
+ .get = sd_getinfrared,
+ },
+};
+
+/* table of the disabled controls */
+static __u32 ctrl_dis[] = {
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_HV7131R 0 */
+ (1 << VFLIP_IDX),
+ /* SENSOR_MI0360 1 */
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_MO4000 2 */
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_OM6802 3 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX),
+ /* SENSOR_OV7630 4 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_OV7648 5 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_OV7660 6 */
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 4 / 8 + 590,
@@ -231,13 +304,13 @@ static const __u8 sn_ov7630[] = {
static const __u8 sn_ov7648[] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
- 0xa1, 0x6e, 0x18, 0x65, 0x00, 0x00, 0x00, 0x10,
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x82,
+ 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00,
/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
- 0x07, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const __u8 sn_ov7660[] = {
@@ -469,6 +542,53 @@ static const __u8 ov7630_sensor_init[][8] = {
/* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */
{}
};
+
+static const __u8 ov7648_sensor_init[][8] = {
+ {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
+ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10},
+ {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10},
+ {0xc1, 0x21, 0x13, 0xa0, 0x04, 0x84, 0x00, 0x10},
+ {0xd1, 0x21, 0x17, 0x1a, 0x02, 0xba, 0xf4, 0x10},
+ {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x1f, 0x41, 0xc0, 0x80, 0x80, 0x10},
+ {0xd1, 0x21, 0x23, 0xde, 0xa0, 0x80, 0x32, 0x10},
+ {0xd1, 0x21, 0x27, 0xfe, 0xa0, 0x00, 0x91, 0x10},
+ {0xd1, 0x21, 0x2b, 0x00, 0x88, 0x85, 0x80, 0x10},
+ {0xc1, 0x21, 0x2f, 0x9c, 0x00, 0xc4, 0x00, 0x10},
+ {0xd1, 0x21, 0x60, 0xa6, 0x60, 0x88, 0x12, 0x10},
+ {0xd1, 0x21, 0x64, 0x88, 0x00, 0x00, 0x94, 0x10},
+ {0xd1, 0x21, 0x68, 0x7a, 0x0c, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x6c, 0x11, 0x33, 0x22, 0x00, 0x10},
+ {0xd1, 0x21, 0x70, 0x11, 0x00, 0x10, 0x50, 0x10},
+ {0xd1, 0x21, 0x74, 0x20, 0x06, 0x00, 0xb5, 0x10},
+ {0xd1, 0x21, 0x78, 0x8a, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x7c, 0x00, 0x43, 0x00, 0x00, 0x10},
+
+ {0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10},
+/* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */
+/* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */
+ {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10},
+/*...*/
+/* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, jfm done */
+ {0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
+/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, * GAIN - def */
+/* {0xb1, 0x21, 0x01, 0x6c, 0x6c, 0x00, 0x00, 0x10}, * B R - def: 80 */
+/*...*/
+ {0xa1, 0x21, 0x11, 0x81, 0x00, 0x00, 0x00, 0x10}, /* CLKRC */
+/* {0xa1, 0x21, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x2a, 0x91, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xb1, 0x21, 0x01, 0x64, 0x84, 0x00, 0x00, 0x10}, * B R - def: 80 */
+
+ {}
+};
+
static const __u8 ov7660_sensor_init[][8] = {
{0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
/* (delay 20ms) */
@@ -557,64 +677,6 @@ static const __u8 ov7660_sensor_init[][8] = {
{0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10},
{}
};
-/* reg 0x04 reg 0x07 reg 0x10 */
-/* expo = (COM1 & 0x02) | ((AECHH & 0x2f) << 10) | (AECh << 2) */
-
-static const __u8 ov7648_sensor_init[][8] = {
- {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
- {0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00},
- {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
- {0xA1, 0x6E, 0x3F, 0x20, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x04, 0x02, 0xB1, 0x02, 0x39, 0x10},
- {0xD1, 0x6E, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x0C, 0x02, 0x7F, 0x01, 0xE0, 0x10},
- {0xD1, 0x6E, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10},
- {0xD1, 0x6E, 0x16, 0x85, 0x40, 0x4A, 0x40, 0x10},
- {0xC1, 0x6E, 0x1A, 0x00, 0x80, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x1D, 0x08, 0x03, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x23, 0x00, 0xB0, 0x00, 0x94, 0x10},
- {0xD1, 0x6E, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x2D, 0x14, 0x35, 0x61, 0x84, 0x10},
- {0xD1, 0x6E, 0x31, 0xA2, 0xBD, 0xD8, 0xFF, 0x10},
- {0xD1, 0x6E, 0x35, 0x06, 0x1E, 0x12, 0x02, 0x10},
- {0xD1, 0x6E, 0x39, 0xAA, 0x53, 0x37, 0xD5, 0x10},
- {0xA1, 0x6E, 0x3D, 0xF2, 0x00, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x3E, 0x00, 0x00, 0x80, 0x03, 0x10},
- {0xD1, 0x6E, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10},
- {0xC1, 0x6E, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10},
- {0xD1, 0x6E, 0x4B, 0x02, 0xEF, 0x08, 0xCD, 0x10},
- {0xD1, 0x6E, 0x4F, 0x00, 0xD0, 0x00, 0xA0, 0x10},
- {0xD1, 0x6E, 0x53, 0x01, 0xAA, 0x01, 0x40, 0x10},
- {0xD1, 0x6E, 0x5A, 0x50, 0x04, 0x30, 0x03, 0x10},
- {0xA1, 0x6E, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x5F, 0x10, 0x40, 0xFF, 0x00, 0x10},
- /* {0xD1, 0x6E, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10},
- * This is currently setting a
- * blue tint, and some things more , i leave it here for future test if
- * somene is having problems with color on this sensor
- {0xD1, 0x6E, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xC1, 0x6E, 0x73, 0x10, 0x80, 0xEB, 0x00, 0x10},
- {0xA1, 0x6E, 0x1E, 0x03, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x15, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xC1, 0x6E, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10},
- {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x07, 0xB5, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x18, 0x6B, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x07, 0xB8, 0x00, 0x00, 0x00, 0x10}, */
- {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
- {0xA1, 0x6E, 0x06, 0x03, 0x00, 0x00, 0x00, 0x10}, /* Bright... */
- {0xA1, 0x6E, 0x07, 0x66, 0x00, 0x00, 0x00, 0x10}, /* B.. */
- {0xC1, 0x6E, 0x1A, 0x03, 0x65, 0x90, 0x00, 0x10}, /* Bright/Witen....*/
-/* {0xC1, 0x6E, 0x16, 0x45, 0x40, 0x60, 0x00, 0x10}, * Bright/Witene */
- {}
-};
static const __u8 qtable4[] = {
0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x08, 0x06,
@@ -757,8 +819,6 @@ static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg)
static int probesensor(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */
msleep(10);
reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */
@@ -770,8 +830,7 @@ static int probesensor(struct gspca_dev *gspca_dev)
&& gspca_dev->usb_buf[3] == 0x00
&& gspca_dev->usb_buf[4] == 0x00) {
PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R");
- sd->sensor = SENSOR_HV7131R;
- return SENSOR_HV7131R;
+ return 0;
}
PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x",
gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
@@ -827,17 +886,20 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
reg_w1(gspca_dev, 0x01, 0x40);
break;
case SENSOR_OV7648:
- reg_w1(gspca_dev, 0x01, 0x43);
- reg_w1(gspca_dev, 0x17, 0xae);
+ reg_w1(gspca_dev, 0x01, 0x63);
+ reg_w1(gspca_dev, 0x17, 0x20);
reg_w1(gspca_dev, 0x01, 0x42);
break;
/*jfm: from win trace */
case SENSOR_OV7660:
- reg_w1(gspca_dev, 0x01, 0x61);
- reg_w1(gspca_dev, 0x17, 0x20);
- reg_w1(gspca_dev, 0x01, 0x60);
- reg_w1(gspca_dev, 0x01, 0x40);
- break;
+ if (sd->bridge == BRIDGE_SN9C120) {
+ reg_w1(gspca_dev, 0x01, 0x61);
+ reg_w1(gspca_dev, 0x17, 0x20);
+ reg_w1(gspca_dev, 0x01, 0x60);
+ reg_w1(gspca_dev, 0x01, 0x40);
+ break;
+ }
+ /* fall thru */
default:
reg_w1(gspca_dev, 0x01, 0x43);
reg_w1(gspca_dev, 0x17, 0x61);
@@ -922,6 +984,13 @@ static void ov7648_InitSensor(struct gspca_dev *gspca_dev)
{
int i = 0;
+ i2c_w8(gspca_dev, ov7648_sensor_init[i]);
+ i++;
+/* win: dble reset */
+ i2c_w8(gspca_dev, ov7648_sensor_init[i]); /* reset */
+ i++;
+ msleep(20);
+/* win: i2c reg read 00..7f */
while (ov7648_sensor_init[i][0]) {
i2c_w8(gspca_dev, ov7648_sensor_init[i]);
i++;
@@ -961,19 +1030,14 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->brightness = BRIGHTNESS_DEF;
sd->contrast = CONTRAST_DEF;
sd->colors = COLOR_DEF;
+ sd->blue = BLUE_BALANCE_DEF;
+ sd->red = RED_BALANCE_DEF;
sd->autogain = AUTOGAIN_DEF;
sd->ag_cnt = -1;
+ sd->vflip = VFLIP_DEF;
+ sd->infrared = INFRARED_DEF;
- switch (sd->sensor) {
- case SENSOR_OV7630:
- case SENSOR_OV7648:
- case SENSOR_OV7660:
- gspca_dev->ctrl_dis = (1 << AUTOGAIN_IDX);
- break;
- }
- if (sd->sensor != SENSOR_OV7630)
- gspca_dev->ctrl_dis |= (1 << VFLIP_IDX);
-
+ gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
return 0;
}
@@ -981,7 +1045,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
static int sd_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
-/* const __u8 *sn9c1xx; */
__u8 regGpio[] = { 0x29, 0x74 };
__u8 regF1;
@@ -1100,32 +1163,13 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev,
return expo;
}
-/* this function is used for sensors o76xx only */
-static void setbrightcont(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int val;
- __u8 reg84_full[0x15];
-
- memcpy(reg84_full, reg84, sizeof reg84_full);
- val = sd->contrast * 0x30 / CONTRAST_MAX + 0x10; /* 10..40 */
- reg84_full[0] = (val + 1) / 2; /* red */
- reg84_full[2] = val; /* green */
- reg84_full[4] = (val + 1) / 5; /* blue */
- val = (sd->brightness - BRIGHTNESS_DEF) * 0x10
- / BRIGHTNESS_MAX;
- reg84_full[0x12] = val & 0x1f; /* 5:0 signed value */
- reg_w(gspca_dev, 0x84, reg84_full, sizeof reg84_full);
-}
-
-/* sensor != ov76xx */
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
unsigned int expo;
__u8 k2;
- k2 = sd->brightness >> 10;
+ k2 = ((int) sd->brightness - 0x8000) >> 10;
switch (sd->sensor) {
case SENSOR_HV7131R:
expo = sd->brightness << 4;
@@ -1147,38 +1191,49 @@ static void setbrightness(struct gspca_dev *gspca_dev)
break;
}
- reg_w1(gspca_dev, 0x96, k2);
+ reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
}
-/* sensor != ov76xx */
static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 k2;
- __u8 contrast[] = { 0x00, 0x00, 0x28, 0x00, 0x07, 0x00 };
-
- k2 = sd->contrast;
- contrast[2] = k2;
- contrast[0] = (k2 + 1) >> 1;
- contrast[4] = (k2 + 1) / 5;
- reg_w(gspca_dev, 0x84, contrast, 6);
+ __u8 contrast[6];
+
+ k2 = sd->contrast * 0x30 / (CONTRAST_MAX + 1) + 0x10; /* 10..40 */
+ contrast[0] = (k2 + 1) / 2; /* red */
+ contrast[1] = 0;
+ contrast[2] = k2; /* green */
+ contrast[3] = 0;
+ contrast[4] = (k2 + 1) / 5; /* blue */
+ contrast[5] = 0;
+ reg_w(gspca_dev, 0x84, contrast, sizeof contrast);
}
static void setcolors(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- __u8 blue, red;
-
- if (sd->colors >= 32) {
- red = 32 + (sd->colors - 32) / 2;
- blue = 64 - sd->colors;
- } else {
- red = sd->colors;
- blue = 32 + (32 - sd->colors) / 2;
+ int i, v;
+ __u8 reg8a[12]; /* U & V gains */
+ static __s16 uv[6] = { /* same as reg84 in signed decimal */
+ -24, -38, 64, /* UR UG UB */
+ 62, -51, -9 /* VR VG VB */
+ };
+ for (i = 0; i < 6; i++) {
+ v = uv[i] * sd->colors / COLOR_DEF;
+ reg8a[i * 2] = v;
+ reg8a[i * 2 + 1] = (v >> 8) & 0x0f;
}
- reg_w1(gspca_dev, 0x05, red);
+ reg_w(gspca_dev, 0x8a, reg8a, sizeof reg8a);
+}
+
+static void setredblue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w1(gspca_dev, 0x05, sd->red);
/* reg_w1(gspca_dev, 0x07, 32); */
- reg_w1(gspca_dev, 0x06, blue);
+ reg_w1(gspca_dev, 0x06, sd->blue);
}
static void setautogain(struct gspca_dev *gspca_dev)
@@ -1195,12 +1250,18 @@ static void setautogain(struct gspca_dev *gspca_dev)
static void setvflip(struct sd *sd)
{
- if (sd->sensor != SENSOR_OV7630)
- return;
i2c_w1(&sd->gspca_dev, 0x75, /* COMN */
sd->vflip ? 0x82 : 0x02);
}
+static void setinfrared(struct sd *sd)
+{
+/*fixme: different sequence for StarCam Clip and StarCam 370i */
+/* Clip */
+ i2c_w1(&sd->gspca_dev, 0x02, /* gpio */
+ sd->infrared ? 0x66 : 0x64);
+}
+
/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
@@ -1235,28 +1296,39 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg17 = 0xe2;
break;
case SENSOR_OV7648:
- reg17 = 0xae;
+ reg17 = 0x20;
break;
/*jfm: from win trace */
case SENSOR_OV7660:
- reg17 = 0xa0;
- break;
+ if (sd->bridge == BRIDGE_SN9C120) {
+ reg17 = 0xa0;
+ break;
+ }
+ /* fall thru */
default:
reg17 = 0x60;
break;
}
reg_w1(gspca_dev, 0x17, reg17);
- reg_w1(gspca_dev, 0x05, sn9c1xx[5]);
- reg_w1(gspca_dev, 0x07, sn9c1xx[7]);
- reg_w1(gspca_dev, 0x06, sn9c1xx[6]);
+/* set reg1 was here */
+ reg_w1(gspca_dev, 0x05, sn9c1xx[5]); /* red */
+ reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */
+ reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */
reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]);
reg_w(gspca_dev, 0x20, gamma_def, sizeof gamma_def);
for (i = 0; i < 8; i++)
reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
switch (sd->sensor) {
- case SENSOR_OV7660:
- reg_w1(gspca_dev, 0x9a, 0x05);
+ case SENSOR_OV7648:
+ reg_w1(gspca_dev, 0x9a, 0x0a);
+ reg_w1(gspca_dev, 0x99, 0x60);
break;
+ case SENSOR_OV7660:
+ if (sd->bridge == BRIDGE_SN9C120) {
+ reg_w1(gspca_dev, 0x9a, 0x05);
+ break;
+ }
+ /* fall thru */
default:
reg_w1(gspca_dev, 0x9a, 0x08);
reg_w1(gspca_dev, 0x99, 0x59);
@@ -1265,10 +1337,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
if (mode)
- reg1 = 0x46; /* 320 clk 48Mhz */
+ reg1 = 0x46; /* 320x240: clk 48Mhz, video trf enable */
else
- reg1 = 0x06; /* 640 clk 24Mz */
- reg17 = 0x61;
+ reg1 = 0x06; /* 640x480: clk 24Mhz, video trf enable */
+ reg17 = 0x61; /* 0x:20: enable sensor clock */
switch (sd->sensor) {
case SENSOR_HV7131R:
hv7131R_InitSensor(gspca_dev);
@@ -1298,23 +1370,21 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
case SENSOR_OV7648:
ov7648_InitSensor(gspca_dev);
- reg17 = 0xa2;
- reg1 = 0x44;
-/* if (mode)
- ; * 320x2...
- else
- ; * 640x... */
+ reg17 = 0x21;
+/* reg1 = 0x42; * 42 - 46? */
break;
default:
/* case SENSOR_OV7660: */
ov7660_InitSensor(gspca_dev);
- if (mode) {
-/* reg17 = 0x21; * 320 */
-/* reg1 = 0x44; */
-/* reg1 = 0x46; (done) */
+ if (sd->bridge == BRIDGE_SN9C120) {
+ if (mode) { /* 320x240 - 160x120 */
+ reg17 = 0xa2;
+ reg1 = 0x44; /* 48 Mhz, video trf eneble */
+ }
} else {
- reg17 = 0xa2; /* 640 */
- reg1 = 0x44;
+ reg17 = 0x22;
+ reg1 = 0x06; /* 24 Mhz, video trf eneble
+ * inverse power down */
}
break;
}
@@ -1342,23 +1412,18 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0x18, reg18);
reg_w1(gspca_dev, 0x17, reg17);
+ reg_w1(gspca_dev, 0x01, reg1);
switch (sd->sensor) {
- case SENSOR_HV7131R:
case SENSOR_MI0360:
- case SENSOR_MO4000:
- case SENSOR_OM6802:
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
+ setinfrared(sd);
break;
case SENSOR_OV7630:
setvflip(sd);
- /* fall thru */
- default: /* OV76xx */
- setbrightcont(gspca_dev);
break;
}
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
setautogain(gspca_dev);
- reg_w1(gspca_dev, 0x01, reg1);
return 0;
}
@@ -1369,6 +1434,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
{ 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 };
static const __u8 stopmi0360[] =
{ 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ static const __u8 stopov7648[] =
+ { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 };
__u8 data;
const __u8 *sn9c1xx;
@@ -1382,8 +1449,10 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
i2c_w8(gspca_dev, stopmi0360);
data = 0x29;
break;
- case SENSOR_OV7630:
case SENSOR_OV7648:
+ i2c_w8(gspca_dev, stopov7648);
+ /* fall thru */
+ case SENSOR_OV7630:
data = 0x29;
break;
default:
@@ -1437,7 +1506,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
expotimes = 0;
sd->exposure = setexposure(gspca_dev,
(unsigned int) expotimes);
- setcolors(gspca_dev);
+ setredblue(gspca_dev);
break;
}
}
@@ -1491,19 +1560,8 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->brightness = val;
- if (gspca_dev->streaming) {
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- case SENSOR_MI0360:
- case SENSOR_MO4000:
- case SENSOR_OM6802:
- setbrightness(gspca_dev);
- break;
- default: /* OV76xx */
- setbrightcont(gspca_dev);
- break;
- }
- }
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
return 0;
}
@@ -1520,19 +1578,8 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->contrast = val;
- if (gspca_dev->streaming) {
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- case SENSOR_MI0360:
- case SENSOR_MO4000:
- case SENSOR_OM6802:
- setcontrast(gspca_dev);
- break;
- default: /* OV76xx */
- setbrightcont(gspca_dev);
- break;
- }
- }
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
return 0;
}
@@ -1562,6 +1609,42 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->blue = val;
+ if (gspca_dev->streaming)
+ setredblue(gspca_dev);
+ return 0;
+}
+
+static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->blue;
+ return 0;
+}
+
+static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->red = val;
+ if (gspca_dev->streaming)
+ setredblue(gspca_dev);
+ return 0;
+}
+
+static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->red;
+ return 0;
+}
+
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1598,6 +1681,24 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+static int sd_setinfrared(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->infrared = val;
+ if (gspca_dev->streaming)
+ setinfrared(sd);
+ return 0;
+}
+
+static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->infrared;
+ return 0;
+}
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
@@ -1620,12 +1721,15 @@ static const __devinitdata struct usb_device_id device_table[] = {
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
{USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)},
{USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)},
+#endif
{USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)},
{USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)},
+#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
{USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)},
- {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)},
#endif
+ {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)},
{USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)},
+ {USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)},
{USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)},
/* bw600.inf:
{USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */
@@ -1649,7 +1753,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
/* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */
{USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/
/*bw600.inf:*/
- {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C110, OV7648, 0x21)}, /*sn9c325?*/
+ {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c110?*/
{USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)},
{USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x21)},
/* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */
@@ -1657,8 +1761,8 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)},
#endif
{USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)},
+ {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)},
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
-/* {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x??)}, */
{USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)},
{USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)},
/* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */
diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
index bca106c153fa..942f04cd44dd 100644
--- a/drivers/media/video/gspca/spca500.c
+++ b/drivers/media/video/gspca/spca500.c
@@ -111,7 +111,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -124,7 +124,7 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 0},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
@@ -633,10 +633,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->subtype = id->driver_info;
if (sd->subtype != LogitechClickSmart310) {
cam->cam_mode = vga_mode;
- cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+ cam->nmodes = ARRAY_SIZE(vga_mode);
} else {
cam->cam_mode = sif_mode;
- cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
+ cam->nmodes = ARRAY_SIZE(sif_mode);
}
sd->qindex = 5;
sd->brightness = BRIGHTNESS_DEF;
diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
index e29954c1c38c..82e3e3e2ada1 100644
--- a/drivers/media/video/gspca/spca501.c
+++ b/drivers/media/video/gspca/spca501.c
@@ -34,6 +34,8 @@ struct sd {
unsigned short contrast;
__u8 brightness;
__u8 colors;
+ __u8 blue_balance;
+ __u8 red_balance;
char subtype;
#define Arowana300KCMOSCamera 0
@@ -52,6 +54,10 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
#define MY_BRIGHTNESS 0
@@ -63,7 +69,7 @@ static struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 127,
.step = 1,
- .default_value = 63,
+ .default_value = 0,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
@@ -75,9 +81,9 @@ static struct ctrl sd_ctrls[] = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Contrast",
.minimum = 0,
- .maximum = 0xffff,
+ .maximum = 64725,
.step = 1,
- .default_value = 0xaa00,
+ .default_value = 64725,
},
.set = sd_setcontrast,
.get = sd_getcontrast,
@@ -91,14 +97,42 @@ static struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 63,
.step = 1,
- .default_value = 31,
+ .default_value = 20,
},
.set = sd_setcolors,
.get = sd_getcolors,
},
+#define MY_BLUE_BALANCE 3
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = sd_setblue_balance,
+ .get = sd_getblue_balance,
+ },
+#define MY_RED_BALANCE 4
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = sd_setred_balance,
+ .get = sd_getred_balance,
+ },
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
@@ -1822,29 +1856,6 @@ static int reg_write(struct usb_device *dev,
return ret;
}
-/* returns: negative is error, pos or zero is data */
-static int reg_read(struct gspca_dev *gspca_dev,
- __u16 req, /* bRequest */
- __u16 index, /* wIndex */
- __u16 length) /* wLength (1 or 2 only) */
-{
- int ret;
-
- gspca_dev->usb_buf[1] = 0;
- ret = usb_control_msg(gspca_dev->dev,
- usb_rcvctrlpipe(gspca_dev->dev, 0),
- req,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, /* value */
- index,
- gspca_dev->usb_buf, length,
- 500); /* timeout */
- if (ret < 0) {
- PDEBUG(D_ERR, "reg_read err %d", ret);
- return -1;
- }
- return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
-}
static int write_vector(struct gspca_dev *gspca_dev,
const __u16 data[][3])
@@ -1869,18 +1880,11 @@ static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->brightness);
reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness);
- reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->brightness);
}
static void getbrightness(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
- __u16 brightness;
-
- brightness = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x11, 2);
- sd->brightness = brightness << 1;
}
static void setcontrast(struct gspca_dev *gspca_dev)
@@ -1895,7 +1899,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
static void getcontrast(struct gspca_dev *gspca_dev)
{
-/* spca50x->contrast = 0xaa01; */
}
static void setcolors(struct gspca_dev *gspca_dev)
@@ -1907,11 +1910,20 @@ static void setcolors(struct gspca_dev *gspca_dev)
static void getcolors(struct gspca_dev *gspca_dev)
{
+}
+
+static void setblue_balance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->blue_balance);
+}
+
+static void setred_balance(struct gspca_dev *gspca_dev)
+{
struct sd *sd = (struct sd *) gspca_dev;
- sd->colors = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x0c, 2);
-/* sd->hue = (reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x13, */
-/* 2) & 0xFF) << 8; */
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->red_balance);
}
/* this function is called at probe time */
@@ -1930,6 +1942,14 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value;
sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
switch (sd->subtype) {
case Arowana300KCMOSCamera:
case SmileIntlCamera:
@@ -1948,15 +1968,17 @@ static int sd_config(struct gspca_dev *gspca_dev,
goto error;
break;
}
+ PDEBUG(D_STREAM, "Initializing SPCA501 finished");
return 0;
error:
return -EINVAL;
}
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+ int mode;
switch (sd->subtype) {
case ThreeComHomeConnectLite:
@@ -1976,14 +1998,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
/* Generic 501 open data */
write_vector(gspca_dev, spca501_open_data);
}
- PDEBUG(D_STREAM, "Initializing SPCA501 finished");
- return 0;
-}
-
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct usb_device *dev = gspca_dev->dev;
- int mode;
/* memorize the wanted pixel format */
mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
@@ -2113,6 +2127,42 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->blue_balance = val;
+ if (gspca_dev->streaming)
+ setblue_balance(gspca_dev);
+ return 0;
+}
+
+static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->blue_balance;
+ return 0;
+}
+
+static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->red_balance = val;
+ if (gspca_dev->streaming)
+ setred_balance(gspca_dev);
+ return 0;
+}
+
+static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->red_balance;
+ return 0;
+}
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
index 895b9fe4018c..2a33a29010ee 100644
--- a/drivers/media/video/gspca/spca505.c
+++ b/drivers/media/video/gspca/spca505.c
@@ -59,7 +59,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
index 645ee9d44d02..96e2512e0621 100644
--- a/drivers/media/video/gspca/spca506.c
+++ b/drivers/media/video/gspca/spca506.c
@@ -110,7 +110,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
index 63ec902c895d..be5d740a315d 100644
--- a/drivers/media/video/gspca/spca508.c
+++ b/drivers/media/video/gspca/spca508.c
@@ -62,7 +62,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
index c3de4e44123d..3c9288019e96 100644
--- a/drivers/media/video/gspca/spca561.c
+++ b/drivers/media/video/gspca/spca561.c
@@ -32,22 +32,22 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- __u16 contrast; /* rev72a only */
-#define CONTRAST_MIN 0x0000
-#define CONTRAST_DEF 0x2000
-#define CONTRAST_MAX 0x3fff
-
__u16 exposure; /* rev12a only */
#define EXPOSURE_MIN 1
#define EXPOSURE_DEF 200
#define EXPOSURE_MAX (4095 - 900) /* see set_exposure */
+ __u8 contrast; /* rev72a only */
+#define CONTRAST_MIN 0x00
+#define CONTRAST_DEF 0x20
+#define CONTRAST_MAX 0x3f
+
__u8 brightness; /* rev72a only */
#define BRIGHTNESS_MIN 0
-#define BRIGHTNESS_DEF 32
-#define BRIGHTNESS_MAX 63
+#define BRIGHTNESS_DEF 0x20
+#define BRIGHTNESS_MAX 0x3f
- __u8 white; /* rev12a only */
+ __u8 white;
#define WHITE_MIN 1
#define WHITE_DEF 0x40
#define WHITE_MAX 0x7f
@@ -73,7 +73,7 @@ struct sd {
#define AG_CNT_START 13
};
-static struct v4l2_pix_format sif_012a_mode[] = {
+static const struct v4l2_pix_format sif_012a_mode[] = {
{160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120,
@@ -96,7 +96,7 @@ static struct v4l2_pix_format sif_012a_mode[] = {
.priv = 0},
};
-static struct v4l2_pix_format sif_072a_mode[] = {
+static const struct v4l2_pix_format sif_072a_mode[] = {
{160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120,
@@ -146,98 +146,7 @@ static struct v4l2_pix_format sif_072a_mode[] = {
#define SPCA561_SNAPBIT 0x20
#define SPCA561_SNAPCTRL 0x40
-static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value)
-{
- int ret;
-
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- 0, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, NULL, 0, 500);
- PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
- if (ret < 0)
- PDEBUG(D_ERR, "reg write: error %d", ret);
-}
-
-static void write_vector(struct gspca_dev *gspca_dev,
- const __u16 data[][2])
-{
- struct usb_device *dev = gspca_dev->dev;
- int i;
-
- i = 0;
- while (data[i][1] != 0) {
- reg_w_val(dev, data[i][1], data[i][0]);
- i++;
- }
-}
-
-/* read 'len' bytes to gspca_dev->usb_buf */
-static void reg_r(struct gspca_dev *gspca_dev,
- __u16 index, __u16 length)
-{
- usb_control_msg(gspca_dev->dev,
- usb_rcvctrlpipe(gspca_dev->dev, 0),
- 0, /* request */
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, /* value */
- index, gspca_dev->usb_buf, length, 500);
-}
-
-static void reg_w_buf(struct gspca_dev *gspca_dev,
- __u16 index, const __u8 *buffer, __u16 len)
-{
- memcpy(gspca_dev->usb_buf, buffer, len);
- usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, /* value */
- index, gspca_dev->usb_buf, len, 500);
-}
-
-static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg)
-{
- int retry = 60;
- __u8 DataLow;
- __u8 DataHight;
-
- DataLow = valeur;
- DataHight = valeur >> 8;
- reg_w_val(gspca_dev->dev, 0x8801, reg);
- reg_w_val(gspca_dev->dev, 0x8805, DataLow);
- reg_w_val(gspca_dev->dev, 0x8800, DataHight);
- while (retry--) {
- reg_r(gspca_dev, 0x8803, 1);
- if (!gspca_dev->usb_buf[0])
- break;
- }
-}
-
-static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
-{
- int retry = 60;
- __u8 value;
- __u8 vallsb;
-
- reg_w_val(gspca_dev->dev, 0x8804, 0x92);
- reg_w_val(gspca_dev->dev, 0x8801, reg);
- reg_w_val(gspca_dev->dev, 0x8802, (mode | 0x01));
- do {
- reg_r(gspca_dev, 0x8803, 1);
- if (!gspca_dev->usb_buf[0])
- break;
- } while (--retry);
- if (retry == 0)
- return -1;
- reg_r(gspca_dev, 0x8800, 1);
- value = gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8805, 1);
- vallsb = gspca_dev->usb_buf[0];
- return ((int) value << 8) | vallsb;
-}
-
-static const __u16 spca561_init_data[][2] = {
+static const __u16 rev72a_init_data1[][2] = {
{0x0000, 0x8114}, /* Software GPIO output data */
{0x0001, 0x8114}, /* Software GPIO output data */
{0x0000, 0x8112}, /* Some kind of reset */
@@ -247,44 +156,26 @@ static const __u16 spca561_init_data[][2] = {
{0x0001, 0x8118}, /* Conf sensor */
{0x0092, 0x8804}, /* I know nothing about these */
{0x0010, 0x8802}, /* 0x88xx registers, so I won't */
- /***************/
{0x000d, 0x8805}, /* sensor default setting */
- {0x0001, 0x8801}, /* 1 <- 0x0d */
- {0x0000, 0x8800},
- {0x0018, 0x8805},
- {0x0002, 0x8801}, /* 2 <- 0x18 */
- {0x0000, 0x8800},
- {0x0065, 0x8805},
- {0x0004, 0x8801}, /* 4 <- 0x01 0x65 */
- {0x0001, 0x8800},
- {0x0021, 0x8805},
- {0x0005, 0x8801}, /* 5 <- 0x21 */
- {0x0000, 0x8800},
- {0x00aa, 0x8805},
- {0x0007, 0x8801}, /* 7 <- 0xaa */
- {0x0000, 0x8800},
- {0x0004, 0x8805},
- {0x0020, 0x8801}, /* 0x20 <- 0x15 0x04 */
- {0x0015, 0x8800},
- {0x0002, 0x8805},
- {0x0039, 0x8801}, /* 0x39 <- 0x02 */
- {0x0000, 0x8800},
- {0x0010, 0x8805},
- {0x0035, 0x8801}, /* 0x35 <- 0x10 */
- {0x0000, 0x8800},
- {0x0049, 0x8805},
- {0x0009, 0x8801}, /* 0x09 <- 0x10 0x49 */
- {0x0010, 0x8800},
- {0x000b, 0x8805},
- {0x0028, 0x8801}, /* 0x28 <- 0x0b */
- {0x0000, 0x8800},
- {0x000f, 0x8805},
- {0x003b, 0x8801}, /* 0x3b <- 0x0f */
- {0x0000, 0x8800},
- {0x0000, 0x8805},
- {0x003c, 0x8801}, /* 0x3c <- 0x00 */
- {0x0000, 0x8800},
- /***************/
+ {}
+};
+static const __u16 rev72a_init_sensor1[][2] = {
+ /* ms-win values */
+ {0x0001, 0x0018}, /* 0x01 <- 0x0d */
+ {0x0002, 0x0065}, /* 0x02 <- 0x18 */
+ {0x0004, 0x0121}, /* 0x04 <- 0x0165 */
+ {0x0005, 0x00aa}, /* 0x05 <- 0x21 */
+ {0x0007, 0x0004}, /* 0x07 <- 0xaa */
+ {0x0020, 0x1502}, /* 0x20 <- 0x1504 */
+ {0x0039, 0x0010}, /* 0x39 <- 0x02 */
+ {0x0035, 0x0049}, /* 0x35 <- 0x10 */
+ {0x0009, 0x100b}, /* 0x09 <- 0x1049 */
+ {0x0028, 0x000f}, /* 0x28 <- 0x0b */
+ {0x003b, 0x003c}, /* 0x3b <- 0x0f */
+ {0x003c, 0x0000}, /* 0x3c <- 0x00 */
+ {}
+};
+static const __u16 rev72a_init_data2[][2] = {
{0x0018, 0x8601}, /* Pixel/line selection for color separation */
{0x0000, 0x8602}, /* Optical black level for user setting */
{0x0060, 0x8604}, /* Optical black horizontal offset */
@@ -309,10 +200,11 @@ static const __u16 spca561_init_data[][2] = {
{0x0004, 0x8612}, /* Gr offset for white balance */
{0x0007, 0x8613}, /* B offset for white balance */
{0x0000, 0x8614}, /* Gb offset for white balance */
- {0x008c, 0x8651}, /* R gain for white balance */
- {0x008c, 0x8652}, /* Gr gain for white balance */
- {0x00b5, 0x8653}, /* B gain for white balance */
- {0x008c, 0x8654}, /* Gb gain for white balance */
+/* from ms-win */
+ {0x0035, 0x8651}, /* R gain for white balance */
+ {0x0040, 0x8652}, /* Gr gain for white balance */
+ {0x005f, 0x8653}, /* B gain for white balance */
+ {0x0040, 0x8654}, /* Gb gain for white balance */
{0x0002, 0x8502}, /* Maximum average bit rate stuff */
{0x0011, 0x8802},
@@ -324,29 +216,22 @@ static const __u16 spca561_init_data[][2] = {
{0x0002, 0x865b}, /* Horizontal offset for valid pixels */
{0x0003, 0x865c}, /* Vertical offset for valid lines */
- /***************//* sensor active */
- {0x0003, 0x8801}, /* 0x03 <- 0x01 0x21 //289 */
- {0x0021, 0x8805},
- {0x0001, 0x8800},
- {0x0004, 0x8801}, /* 0x04 <- 0x01 0x65 //357 */
- {0x0065, 0x8805},
- {0x0001, 0x8800},
- {0x0005, 0x8801}, /* 0x05 <- 0x2f */
- {0x002f, 0x8805},
- {0x0000, 0x8800},
- {0x0006, 0x8801}, /* 0x06 <- 0 */
- {0x0000, 0x8805},
- {0x0000, 0x8800},
- {0x000a, 0x8801}, /* 0x0a <- 2 */
- {0x0002, 0x8805},
- {0x0000, 0x8800},
- {0x0009, 0x8801}, /* 0x09 <- 0x1061 */
- {0x0061, 0x8805},
- {0x0010, 0x8800},
- {0x0035, 0x8801}, /* 0x35 <-0x14 */
- {0x0014, 0x8805},
- {0x0000, 0x8800},
+ {}
+};
+static const __u16 rev72a_init_sensor2[][2] = {
+ /* ms-win values */
+ {0x0003, 0x0121}, /* 0x03 <- 0x01 0x21 //289 */
+ {0x0004, 0x0165}, /* 0x04 <- 0x01 0x65 //357 */
+ {0x0005, 0x002f}, /* 0x05 <- 0x2f */
+ {0x0006, 0x0000}, /* 0x06 <- 0 */
+ {0x000a, 0x0002}, /* 0x0a <- 2 */
+ {0x0009, 0x1061}, /* 0x09 <- 0x1061 */
+ {0x0035, 0x0014}, /* 0x35 <- 0x14 */
+ {}
+};
+static const __u16 rev72a_init_data3[][2] = {
{0x0030, 0x8112}, /* ISO and drop packet enable */
+/*fixme: should stop here*/
{0x0000, 0x8112}, /* Some kind of reset ???? */
{0x0009, 0x8118}, /* Enable sensor and set standby */
{0x0000, 0x8114}, /* Software GPIO output data */
@@ -434,7 +319,6 @@ static const __u16 spca561_init_data[][2] = {
{}
};
-
/******************** QC Express etch2 stuff ********************/
static const __u16 Pb100_1map8300[][2] = {
/* reg, value */
@@ -515,22 +399,112 @@ static const __u16 spca561_161rev12A_data2[][2] = {
{}
};
-static void sensor_mapwrite(struct gspca_dev *gspca_dev,
- const __u16 sensormap[][2])
+static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value)
+{
+ int ret;
+
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
+ if (ret < 0)
+ PDEBUG(D_ERR, "reg write: error %d", ret);
+}
+
+static void write_vector(struct gspca_dev *gspca_dev,
+ const __u16 data[][2])
{
- int i = 0;
- __u8 usbval[2];
+ struct usb_device *dev = gspca_dev->dev;
+ int i;
- while (sensormap[i][0]) {
- usbval[0] = sensormap[i][1];
- usbval[1] = sensormap[i][1] >> 8;
- reg_w_buf(gspca_dev, sensormap[i][0], usbval, 2);
+ i = 0;
+ while (data[i][1] != 0) {
+ reg_w_val(dev, data[i][1], data[i][0]);
i++;
}
}
+
+/* read 'len' bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ __u16 index, __u16 length)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, length, 500);
+}
+
+/* write 'len' bytes from gspca_dev->usb_buf */
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ __u16 index, __u16 len)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, len, 500);
+}
+
+static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg)
+{
+ int retry = 60;
+
+ reg_w_val(gspca_dev->dev, 0x8801, reg);
+ reg_w_val(gspca_dev->dev, 0x8805, value);
+ reg_w_val(gspca_dev->dev, 0x8800, value >> 8);
+ do {
+ reg_r(gspca_dev, 0x8803, 1);
+ if (!gspca_dev->usb_buf[0])
+ return;
+ } while (--retry);
+}
+
+static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
+{
+ int retry = 60;
+ __u8 value;
+
+ reg_w_val(gspca_dev->dev, 0x8804, 0x92);
+ reg_w_val(gspca_dev->dev, 0x8801, reg);
+ reg_w_val(gspca_dev->dev, 0x8802, mode | 0x01);
+ do {
+ reg_r(gspca_dev, 0x8803, 1);
+ if (!gspca_dev->usb_buf[0]) {
+ reg_r(gspca_dev, 0x8800, 1);
+ value = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0x8805, 1);
+ return ((int) value << 8) | gspca_dev->usb_buf[0];
+ }
+ } while (--retry);
+ return -1;
+}
+
+static void sensor_mapwrite(struct gspca_dev *gspca_dev,
+ const __u16 (*sensormap)[2])
+{
+ while ((*sensormap)[0]) {
+ gspca_dev->usb_buf[0] = (*sensormap)[1];
+ gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8;
+ reg_w_buf(gspca_dev, (*sensormap)[0], 2);
+ sensormap++;
+ }
+}
+
+static void write_sensor_72a(struct gspca_dev *gspca_dev,
+ const __u16 (*sensor)[2])
+{
+ while ((*sensor)[0]) {
+ i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]);
+ sensor++;
+ }
+}
+
static void init_161rev12A(struct gspca_dev *gspca_dev)
{
-/* sensor_reset(gspca_dev); (not in win) */
write_vector(gspca_dev, spca561_161rev12A_data1);
sensor_mapwrite(gspca_dev, Pb100_1map8300);
/*fixme: should be in sd_start*/
@@ -598,49 +572,68 @@ static int sd_init_12a(struct gspca_dev *gspca_dev)
static int sd_init_72a(struct gspca_dev *gspca_dev)
{
PDEBUG(D_STREAM, "Chip revision: 072a");
- write_vector(gspca_dev, spca561_init_data);
+ write_vector(gspca_dev, rev72a_init_data1);
+ write_sensor_72a(gspca_dev, rev72a_init_sensor1);
+ write_vector(gspca_dev, rev72a_init_data2);
+ write_sensor_72a(gspca_dev, rev72a_init_sensor2);
+ write_vector(gspca_dev, rev72a_init_data3);
return 0;
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+/* rev 72a only */
+static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
- __u8 lowb;
+ __u8 value;
- switch (sd->chip_revision) {
- case Rev072A:
- lowb = sd->contrast >> 8;
- reg_w_val(dev, 0x8651, lowb);
- reg_w_val(dev, 0x8652, lowb);
- reg_w_val(dev, 0x8653, lowb);
- reg_w_val(dev, 0x8654, lowb);
- break;
- default: {
-/* case Rev012A: { */
- static const __u8 Reg8391[] =
- { 0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00 };
+ value = sd->brightness;
- reg_w_buf(gspca_dev, 0x8391, Reg8391, 8);
- reg_w_buf(gspca_dev, 0x8390, Reg8391, 8);
- break;
- }
- }
+ /* offsets for white balance */
+ reg_w_val(dev, 0x8611, value); /* R */
+ reg_w_val(dev, 0x8612, value); /* Gr */
+ reg_w_val(dev, 0x8613, value); /* B */
+ reg_w_val(dev, 0x8614, value); /* Gb */
}
-/* rev12a only */
static void setwhite(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u16 white;
- __u8 reg8614, reg8616;
+ __u8 blue, red;
+ __u16 reg;
- white = sd->white;
/* try to emulate MS-win as possible */
- reg8616 = 0x90 - white * 5 / 8;
- reg_w_val(gspca_dev->dev, 0x8616, reg8616);
- reg8614 = 0x20 + white * 3 / 8;
- reg_w_val(gspca_dev->dev, 0x8614, reg8614);
+ white = sd->white;
+ red = 0x20 + white * 3 / 8;
+ blue = 0x90 - white * 5 / 8;
+ if (sd->chip_revision == Rev012A) {
+ reg = 0x8614;
+ } else {
+ reg = 0x8651;
+ red += sd->contrast - 0x20;
+ blue += sd->contrast - 0x20;
+ }
+ reg_w_val(gspca_dev->dev, reg, red);
+ reg_w_val(gspca_dev->dev, reg + 2, blue);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+ __u8 value;
+
+ if (sd->chip_revision != Rev072A)
+ return;
+ value = sd->contrast + 0x20;
+
+ /* gains for white balance */
+ setwhite(gspca_dev);
+/* reg_w_val(dev, 0x8651, value); * R - done by setwhite */
+ reg_w_val(dev, 0x8652, value); /* Gr */
+/* reg_w_val(dev, 0x8653, value); * B - done by setwhite */
+ reg_w_val(dev, 0x8654, value); /* Gb */
}
/* rev 12a only */
@@ -649,7 +642,6 @@ static void setexposure(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
int expo;
int clock_divider;
- __u8 data[2];
/* Register 0x8309 controls exposure for the spca561,
the basic exposure setting goes from 1-2047, where 1 is completely
@@ -673,20 +665,19 @@ static void setexposure(struct gspca_dev *gspca_dev)
clock_divider = 3;
}
expo |= clock_divider << 11;
- data[0] = expo;
- data[1] = expo >> 8;
- reg_w_buf(gspca_dev, 0x8309, data, 2);
+ gspca_dev->usb_buf[0] = expo;
+ gspca_dev->usb_buf[1] = expo >> 8;
+ reg_w_buf(gspca_dev, 0x8309, 2);
}
/* rev 12a only */
static void setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- __u8 data[2];
- data[0] = sd->gain;
- data[1] = 0;
- reg_w_buf(gspca_dev, 0x8335, data, 2);
+ gspca_dev->usb_buf[0] = sd->gain;
+ gspca_dev->usb_buf[1] = 0;
+ reg_w_buf(gspca_dev, 0x8335, 2);
}
static void setautogain(struct gspca_dev *gspca_dev)
@@ -702,9 +693,9 @@ static void setautogain(struct gspca_dev *gspca_dev)
static int sd_start_12a(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
- int Clck = 0x8a; /* lower 0x8X values lead to fps > 30 */
- __u8 Reg8307[] = { 0xaa, 0x00 };
int mode;
+ static const __u8 Reg8391[8] =
+ {0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00};
mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
if (mode <= 1) {
@@ -716,14 +707,21 @@ static int sd_start_12a(struct gspca_dev *gspca_dev)
* is sufficient to push raw frames at ~20fps */
reg_w_val(dev, 0x8500, mode);
} /* -- qq@kuku.eu.org */
- reg_w_buf(gspca_dev, 0x8307, Reg8307, 2);
- reg_w_val(gspca_dev->dev, 0x8700, Clck);
+
+ gspca_dev->usb_buf[0] = 0xaa;
+ gspca_dev->usb_buf[1] = 0x00;
+ reg_w_buf(gspca_dev, 0x8307, 2);
+ /* clock - lower 0x8X values lead to fps > 30 */
+ reg_w_val(gspca_dev->dev, 0x8700, 0x8a);
/* 0x8f 0x85 0x27 clock */
reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20);
reg_w_val(gspca_dev->dev, 0x850b, 0x03);
- setcontrast(gspca_dev);
+ memcpy(gspca_dev->usb_buf, Reg8391, 8);
+ reg_w_buf(gspca_dev, 0x8391, 8);
+ reg_w_buf(gspca_dev, 0x8390, 8);
setwhite(gspca_dev);
setautogain(gspca_dev);
+/* setgain(gspca_dev); */
setexposure(gspca_dev);
return 0;
}
@@ -750,6 +748,9 @@ static int sd_start_72a(struct gspca_dev *gspca_dev)
reg_w_val(dev, 0x8500, mode); /* mode */
reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */
reg_w_val(dev, 0x8112, 0x10 | 0x20);
+ setcontrast(gspca_dev);
+/* setbrightness(gspca_dev); * fixme: bad values */
+ setwhite(gspca_dev);
setautogain(gspca_dev);
return 0;
}
@@ -791,7 +792,6 @@ static void do_autogain(struct gspca_dev *gspca_dev)
__u8 luma_mean = 110;
__u8 luma_delta = 20;
__u8 spring = 4;
- __u8 reg8339[2];
if (sd->ag_cnt < 0)
return;
@@ -834,13 +834,13 @@ static void do_autogain(struct gspca_dev *gspca_dev)
if (gainG > 0x3f)
gainG = 0x3f;
- else if (gainG < 4)
+ else if (gainG < 3)
gainG = 3;
i2c_write(gspca_dev, gainG, 0x35);
- if (expotimes >= 0x0256)
+ if (expotimes > 0x0256)
expotimes = 0x0256;
- else if (expotimes < 4)
+ else if (expotimes < 3)
expotimes = 3;
i2c_write(gspca_dev, expotimes | pixelclk, 0x09);
}
@@ -848,13 +848,13 @@ static void do_autogain(struct gspca_dev *gspca_dev)
case Rev012A:
reg_r(gspca_dev, 0x8330, 2);
if (gspca_dev->usb_buf[1] > 0x08) {
- reg8339[0] = ++sd->expo12a;
- reg8339[1] = 0;
- reg_w_buf(gspca_dev, 0x8339, reg8339, 2);
+ gspca_dev->usb_buf[0] = ++sd->expo12a;
+ gspca_dev->usb_buf[1] = 0;
+ reg_w_buf(gspca_dev, 0x8339, 2);
} else if (gspca_dev->usb_buf[1] < 0x02) {
- reg8339[0] = --sd->expo12a;
- reg8339[1] = 0;
- reg_w_buf(gspca_dev, 0x8339, reg8339, 2);
+ gspca_dev->usb_buf[0] = --sd->expo12a;
+ gspca_dev->usb_buf[1] = 0;
+ reg_w_buf(gspca_dev, 0x8339, 2);
}
break;
}
@@ -867,8 +867,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
- switch (data[0]) {
- case 0: /* start of frame */
+ switch (data[0]) { /* sequence number */
+ case 0: /* start of frame */
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
data, 0);
data += SPCA561_OFFSET_DATA;
@@ -890,8 +890,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
frame, data, len);
}
return;
- case 0xff: /* drop */
-/* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ case 0xff: /* drop (empty mpackets) */
return;
}
data++;
@@ -900,55 +899,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
/* rev 72a only */
-static void setbrightness(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- __u8 value;
-
- value = sd->brightness;
- reg_w_val(gspca_dev->dev, 0x8611, value);
- reg_w_val(gspca_dev->dev, 0x8612, value);
- reg_w_val(gspca_dev->dev, 0x8613, value);
- reg_w_val(gspca_dev->dev, 0x8614, value);
-}
-
-static void getbrightness(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- __u16 tot;
-
- tot = 0;
- reg_r(gspca_dev, 0x8611, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8612, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8613, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8614, 1);
- tot += gspca_dev->usb_buf[0];
- sd->brightness = tot >> 2;
-}
-
-/* rev72a only */
-static void getcontrast(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- __u16 tot;
-
- tot = 0;
- reg_r(gspca_dev, 0x8651, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8652, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8653, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8654, 1);
- tot += gspca_dev->usb_buf[0];
- sd->contrast = tot << 6;
- PDEBUG(D_CONF, "get contrast %d", sd->contrast);
-}
-
-/* rev 72a only */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -963,7 +913,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
- getbrightness(gspca_dev);
*val = sd->brightness;
return 0;
}
@@ -983,7 +932,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
- getcontrast(gspca_dev);
*val = sd->contrast;
return 0;
}
@@ -1006,7 +954,6 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
-/* rev12a only */
static int sd_setwhite(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1121,6 +1068,19 @@ static struct ctrl sd_ctrls_12a[] = {
static struct ctrl sd_ctrls_72a[] = {
{
+ {
+ .id = V4L2_CID_DO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "White Balance",
+ .minimum = WHITE_MIN,
+ .maximum = WHITE_MAX,
+ .step = 1,
+ .default_value = WHITE_DEF,
+ },
+ .set = sd_setwhite,
+ .get = sd_getwhite,
+ },
+ {
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
index d9d64911f22a..60de9af87fbb 100644
--- a/drivers/media/video/gspca/stk014.c
+++ b/drivers/media/video/gspca/stk014.c
@@ -109,7 +109,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -424,10 +424,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* beginning of the frame */
#define STKHDRSZ 12
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
- data + STKHDRSZ, len - STKHDRSZ);
-#undef STKHDRSZ
- return;
+ data += STKHDRSZ;
+ len -= STKHDRSZ;
}
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
diff --git a/drivers/media/video/gspca/stv06xx/Kconfig b/drivers/media/video/gspca/stv06xx/Kconfig
new file mode 100644
index 000000000000..634ad38d9fb8
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/Kconfig
@@ -0,0 +1,9 @@
+config USB_STV06XX
+ tristate "STV06XX USB Camera Driver"
+ depends on USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on
+ the ST STV06XX chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stv06xx.
diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile
new file mode 100644
index 000000000000..feeaa94ab588
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/Makefile
@@ -0,0 +1,9 @@
+obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o
+
+gspca_stv06xx-objs := stv06xx.o \
+ stv06xx_vv6410.o \
+ stv06xx_hdcs.o \
+ stv06xx_pb0100.o
+
+EXTRA_CFLAGS += -Idrivers/media/video/gspca
+
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c
new file mode 100644
index 000000000000..13a021e3cbb7
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#include "stv06xx_sensor.h"
+
+MODULE_AUTHOR("Erik Andrén");
+MODULE_DESCRIPTION("STV06XX USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+static int dump_bridge;
+static int dump_sensor;
+
+int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+ u8 len = (i2c_data > 0xff) ? 2 : 1;
+
+ buf[0] = i2c_data & 0xff;
+ buf[1] = (i2c_data >> 8) & 0xff;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, address, 0, buf, len,
+ STV06XX_URB_MSG_TIMEOUT);
+
+
+ PDEBUG(D_CONF, "Written 0x%x to address 0x%x, status: %d",
+ i2c_data, address, err);
+
+ return (err < 0) ? err : 0;
+}
+
+int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x04, 0xc0, address, 0, buf, 1,
+ STV06XX_URB_MSG_TIMEOUT);
+
+ *i2c_data = buf[0];
+
+ PDEBUG(D_CONF, "Read 0x%x from address 0x%x, status %d",
+ *i2c_data, address, err);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Wraps the normal write sensor bytes / words functions for writing a
+ single value */
+int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value)
+{
+ if (sd->sensor->i2c_len == 2) {
+ u16 data[2] = { address, value };
+ return stv06xx_write_sensor_words(sd, data, 1);
+ } else {
+ u8 data[2] = { address, value };
+ return stv06xx_write_sensor_bytes(sd, data, 1);
+ }
+}
+
+static int stv06xx_write_sensor_finish(struct sd *sd)
+{
+ int err = 0;
+
+ if (IS_850(sd)) {
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* Quickam Web needs an extra packet */
+ buf[0] = 0;
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x1704, 0, buf, 1,
+ STV06XX_URB_MSG_TIMEOUT);
+ }
+
+ return (err < 0) ? err : 0;
+}
+
+int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len)
+{
+ int err, i, j;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ PDEBUG(D_USBO, "I2C: Command buffer contains %d entries", len);
+ for (i = 0; i < len;) {
+ /* Build the command buffer */
+ memset(buf, 0, I2C_BUFFER_LENGTH);
+ for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) {
+ buf[j] = data[2*i];
+ buf[0x10 + j] = data[2*i+1];
+ PDEBUG(D_USBO, "I2C: Writing 0x%02x to reg 0x%02x",
+ data[2*i+1], data[2*i]);
+ }
+ buf[0x20] = sd->sensor->i2c_addr;
+ buf[0x21] = j - 1; /* Number of commands to send - 1 */
+ buf[0x22] = I2C_WRITE_CMD;
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x0400, 0, buf,
+ I2C_BUFFER_LENGTH,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (err < 0)
+ return err;
+ }
+ return stv06xx_write_sensor_finish(sd);
+}
+
+int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len)
+{
+ int err, i, j;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ PDEBUG(D_USBO, "I2C: Command buffer contains %d entries", len);
+
+ for (i = 0; i < len;) {
+ /* Build the command buffer */
+ memset(buf, 0, I2C_BUFFER_LENGTH);
+ for (j = 0; j < I2C_MAX_WORDS && i < len; j++, i++) {
+ buf[j] = data[2*i];
+ buf[0x10 + j * 2] = data[2*i+1];
+ buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8;
+ PDEBUG(D_USBO, "I2C: Writing 0x%04x to reg 0x%02x",
+ data[2*i+1], data[2*i]);
+ }
+ buf[0x20] = sd->sensor->i2c_addr;
+ buf[0x21] = j - 1; /* Number of commands to send - 1 */
+ buf[0x22] = I2C_WRITE_CMD;
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x0400, 0, buf,
+ I2C_BUFFER_LENGTH,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (err < 0)
+ return err;
+ }
+ return stv06xx_write_sensor_finish(sd);
+}
+
+int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ err = stv06xx_write_bridge(sd, STV_I2C_FLUSH, sd->sensor->i2c_flush);
+ if (err < 0)
+ return err;
+
+ /* Clear mem */
+ memset(buf, 0, I2C_BUFFER_LENGTH);
+
+ buf[0] = address;
+ buf[0x20] = sd->sensor->i2c_addr;
+ buf[0x21] = 0;
+
+ /* Read I2C register */
+ buf[0x22] = I2C_READ_CMD;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (err < 0) {
+ PDEBUG(D_ERR, "I2C Read: error writing address: %d", err);
+ return err;
+ }
+
+ err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x04, 0xc0, 0x1410, 0, buf, sd->sensor->i2c_len,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (sd->sensor->i2c_len == 2)
+ *value = buf[0] | (buf[1] << 8);
+ else
+ *value = buf[0];
+
+ PDEBUG(D_USBO, "I2C: Read 0x%x from address 0x%x, status: %d",
+ *value, address, err);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Dumps all bridge registers */
+static void stv06xx_dump_bridge(struct sd *sd)
+{
+ int i;
+ u8 data, buf;
+
+ info("Dumping all stv06xx bridge registers");
+ for (i = 0x1400; i < 0x160f; i++) {
+ stv06xx_read_bridge(sd, i, &data);
+
+ info("Read 0x%x from address 0x%x", data, i);
+ }
+
+ for (i = 0x1400; i < 0x160f; i++) {
+ stv06xx_read_bridge(sd, i, &data);
+ buf = data;
+
+ stv06xx_write_bridge(sd, i, 0xff);
+ stv06xx_read_bridge(sd, i, &data);
+ if (data == 0xff)
+ info("Register 0x%x is read/write", i);
+ else if (data != buf)
+ info("Register 0x%x is read/write,"
+ "but only partially", i);
+ else
+ info("Register 0x%x is read-only", i);
+
+ stv06xx_write_bridge(sd, i, buf);
+ }
+}
+
+/* this function is called at probe and resume time */
+static int stv06xx_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+
+ PDEBUG(D_PROBE, "Initializing camera");
+
+ /* Let the usb init settle for a bit
+ before performing the initialization */
+ msleep(250);
+
+ err = sd->sensor->init(sd);
+
+ if (dump_sensor)
+ sd->sensor->dump(sd);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Start the camera */
+static int stv06xx_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+
+ /* Prepare the sensor for start */
+ err = sd->sensor->start(sd);
+ if (err < 0)
+ goto out;
+
+ /* Start isochronous streaming */
+ err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 1);
+
+out:
+ if (err < 0)
+ PDEBUG(D_STREAM, "Starting stream failed");
+ else
+ PDEBUG(D_STREAM, "Started streaming");
+
+ return (err < 0) ? err : 0;
+}
+
+static void stv06xx_stopN(struct gspca_dev *gspca_dev)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* stop ISO-streaming */
+ err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 0);
+ if (err < 0)
+ goto out;
+
+ err = sd->sensor->stop(sd);
+ if (err < 0)
+ goto out;
+
+out:
+ if (err < 0)
+ PDEBUG(D_STREAM, "Failed to stop stream");
+ else
+ PDEBUG(D_STREAM, "Stopped streaming");
+}
+
+/*
+ * Analyse an USB packet of the data stream and store it appropriately.
+ * Each packet contains an integral number of chunks. Each chunk has
+ * 2-bytes identification, followed by 2-bytes that describe the chunk
+ * length. Known/guessed chunk identifications are:
+ * 8001/8005/C001/C005 - Begin new frame
+ * 8002/8006/C002/C006 - End frame
+ * 0200/4200 - Contains actual image data, bayer or compressed
+ * 0005 - 11 bytes of unknown data
+ * 0100 - 2 bytes of unknown data
+ * The 0005 and 0100 chunks seem to appear only in compressed stream.
+ */
+static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+ __u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ PDEBUG(D_PACK, "Packet of length %d arrived", len);
+
+ /* A packet may contain several frames
+ loop until the whole packet is reached */
+ while (len) {
+ int id, chunk_len;
+
+ if (len < 4) {
+ PDEBUG(D_PACK, "Packet is smaller than 4 bytes");
+ return;
+ }
+
+ /* Capture the id */
+ id = (data[0] << 8) | data[1];
+
+ /* Capture the chunk length */
+ chunk_len = (data[2] << 8) | data[3];
+ PDEBUG(D_PACK, "Chunk id: %x, length: %d", id, chunk_len);
+
+ data += 4;
+ len -= 4;
+
+ if (len < chunk_len) {
+ PDEBUG(D_ERR, "URB packet length is smaller"
+ " than the specified chunk length");
+ return;
+ }
+
+ switch (id) {
+ case 0x0200:
+ case 0x4200:
+ PDEBUG(D_PACK, "Frame data packet detected");
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ data, chunk_len);
+ break;
+
+ case 0x8001:
+ case 0x8005:
+ case 0xc001:
+ case 0xc005:
+ PDEBUG(D_PACK, "Starting new frame");
+
+ /* Create a new frame, chunk length should be zero */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ frame, data, 0);
+
+ if (chunk_len)
+ PDEBUG(D_ERR, "Chunk length is "
+ "non-zero on a SOF");
+ break;
+
+ case 0x8002:
+ case 0x8006:
+ case 0xc002:
+ PDEBUG(D_PACK, "End of frame detected");
+
+ /* Complete the last frame (if any) */
+ gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0);
+
+ if (chunk_len)
+ PDEBUG(D_ERR, "Chunk length is "
+ "non-zero on a EOF");
+ break;
+
+ case 0x0005:
+ PDEBUG(D_PACK, "Chunk 0x005 detected");
+ /* Unknown chunk with 11 bytes of data,
+ occurs just before end of each frame
+ in compressed mode */
+ break;
+
+ case 0x0100:
+ PDEBUG(D_PACK, "Chunk 0x0100 detected");
+ /* Unknown chunk with 2 bytes of data,
+ occurs 2-3 times per USB interrupt */
+ break;
+ default:
+ PDEBUG(D_PACK, "Unknown chunk %d detected", id);
+ /* Unknown chunk */
+ }
+ data += chunk_len;
+ len -= chunk_len;
+ }
+}
+
+static int stv06xx_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = stv06xx_config,
+ .init = stv06xx_init,
+ .start = stv06xx_start,
+ .stopN = stv06xx_stopN,
+ .pkt_scan = stv06xx_pkt_scan
+};
+
+/* This function is called at probe time */
+static int stv06xx_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ PDEBUG(D_PROBE, "Configuring camera");
+
+ cam = &gspca_dev->cam;
+ cam->epaddr = STV_ISOC_ENDPOINT_ADDR;
+ sd->desc = sd_desc;
+ gspca_dev->sd_desc = &sd->desc;
+
+ if (dump_bridge)
+ stv06xx_dump_bridge(sd);
+
+ sd->sensor = &stv06xx_sensor_vv6410;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_hdcs1x00;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_hdcs1020;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_pb0100;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = NULL;
+ return -ENODEV;
+}
+
+
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x046d, 0x0840)}, /* QuickCam Express */
+ {USB_DEVICE(0x046d, 0x0850)}, /* LEGO cam / QuickCam Web */
+ {USB_DEVICE(0x046d, 0x0870)}, /* Dexxa WebCam USB */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ PDEBUG(D_PROBE, "Probing for a stv06xx device");
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static void sd_disconnect(struct usb_interface *intf)
+{
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+ struct sd *sd = (struct sd *) gspca_dev;
+ PDEBUG(D_PROBE, "Disconnecting the stv06xx device");
+
+ if (sd->sensor->disconnect)
+ sd->sensor->disconnect(sd);
+ gspca_disconnect(intf);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = sd_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ if (usb_register(&sd_driver) < 0)
+ return -1;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
+
+module_param(dump_bridge, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup");
+
+module_param(dump_sensor, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_sensor, "Dumps all sensor registers at startup");
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h
new file mode 100644
index 000000000000..1207e7d17f14
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_H_
+#define STV06XX_H_
+
+#include "gspca.h"
+
+#define MODULE_NAME "STV06xx"
+
+#define STV_ISOC_ENDPOINT_ADDR 0x81
+
+#ifndef V4L2_PIX_FMT_SGRBG8
+#define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G')
+#endif
+
+#define STV_REG23 0x0423
+
+/* Control registers of the STV0600 ASIC */
+#define STV_I2C_PARTNER 0x1420
+#define STV_I2C_VAL_REG_VAL_PAIRS_MIN1 0x1421
+#define STV_I2C_READ_WRITE_TOGGLE 0x1422
+#define STV_I2C_FLUSH 0x1423
+#define STV_I2C_SUCC_READ_REG_VALS 0x1424
+
+#define STV_ISO_ENABLE 0x1440
+#define STV_SCAN_RATE 0x1443
+#define STV_LED_CTRL 0x1445
+#define STV_STV0600_EMULATION 0x1446
+#define STV_REG00 0x1500
+#define STV_REG01 0x1501
+#define STV_REG02 0x1502
+#define STV_REG03 0x1503
+#define STV_REG04 0x1504
+
+#define STV_ISO_SIZE_L 0x15c1
+#define STV_ISO_SIZE_H 0x15c2
+
+/* Refers to the CIF 352x288 and QCIF 176x144 */
+/* 1: 288 lines, 2: 144 lines */
+#define STV_Y_CTRL 0x15c3
+
+/* 0xa: 352 columns, 0x6: 176 columns */
+#define STV_X_CTRL 0x1680
+
+#define STV06XX_URB_MSG_TIMEOUT 5000
+
+#define I2C_MAX_BYTES 16
+#define I2C_MAX_WORDS 8
+
+#define I2C_BUFFER_LENGTH 0x23
+#define I2C_READ_CMD 3
+#define I2C_WRITE_CMD 1
+
+#define LED_ON 1
+#define LED_OFF 0
+
+/* STV06xx device descriptor */
+struct sd {
+ struct gspca_dev gspca_dev;
+
+ /* A pointer to the currently connected sensor */
+ const struct stv06xx_sensor *sensor;
+
+ /* A pointer to the sd_desc struct */
+ struct sd_desc desc;
+
+ /* Sensor private data */
+ void *sensor_priv;
+};
+
+int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data);
+int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data);
+
+int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len);
+int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len);
+
+int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value);
+int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value);
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
new file mode 100644
index 000000000000..14335a9e4bb5
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2008 Chia-I Wu
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#include "stv06xx_hdcs.h"
+
+enum hdcs_power_state {
+ HDCS_STATE_SLEEP,
+ HDCS_STATE_IDLE,
+ HDCS_STATE_RUN
+};
+
+/* no lock? */
+struct hdcs {
+ enum hdcs_power_state state;
+ int w, h;
+
+ /* visible area of the sensor array */
+ struct {
+ int left, top;
+ int width, height;
+ int border;
+ } array;
+
+ struct {
+ /* Column timing overhead */
+ u8 cto;
+ /* Column processing overhead */
+ u8 cpo;
+ /* Row sample period constant */
+ u16 rs;
+ /* Exposure reset duration */
+ u16 er;
+ } exp;
+
+ int psmp;
+};
+
+static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len)
+{
+ u8 regs[I2C_MAX_BYTES * 2];
+ int i;
+
+ if (unlikely((len <= 0) || (len >= I2C_MAX_BYTES) ||
+ (reg + len > 0xff)))
+ return -EINVAL;
+
+ for (i = 0; i < len; i++, reg++) {
+ regs[2*i] = reg;
+ regs[2*i+1] = vals[i];
+ }
+
+ return stv06xx_write_sensor_bytes(sd, regs, len);
+}
+
+static int hdcs_set_state(struct sd *sd, enum hdcs_power_state state)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ u8 val;
+ int ret;
+
+ if (hdcs->state == state)
+ return 0;
+
+ /* we need to go idle before running or sleeping */
+ if (hdcs->state != HDCS_STATE_IDLE) {
+ ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0);
+ if (ret)
+ return ret;
+ }
+
+ hdcs->state = HDCS_STATE_IDLE;
+
+ if (state == HDCS_STATE_IDLE)
+ return 0;
+
+ switch (state) {
+ case HDCS_STATE_SLEEP:
+ val = HDCS_SLEEP_MODE;
+ break;
+
+ case HDCS_STATE_RUN:
+ val = HDCS_RUN_ENABLE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val);
+ if (ret < 0)
+ hdcs->state = state;
+
+ return ret;
+}
+
+static int hdcs_reset(struct sd *sd)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ int err;
+
+ err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 1);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0);
+ if (err < 0)
+ hdcs->state = HDCS_STATE_IDLE;
+
+ return err;
+}
+
+static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct hdcs *hdcs = sd->sensor_priv;
+
+ /* Column time period */
+ int ct;
+ /* Column processing period */
+ int cp;
+ /* Row processing period */
+ int rp;
+ int cycles;
+ int err;
+ int rowexp;
+ u16 data[2];
+
+ err = stv06xx_read_sensor(sd, HDCS_ROWEXPL, &data[0]);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_read_sensor(sd, HDCS_ROWEXPH, &data[1]);
+ if (err < 0)
+ return err;
+
+ rowexp = (data[1] << 8) | data[0];
+
+ ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2);
+ cp = hdcs->exp.cto + (hdcs->w * ct / 2);
+ rp = hdcs->exp.rs + cp;
+
+ cycles = rp * rowexp;
+ *val = cycles / HDCS_CLK_FREQ_MHZ;
+ PDEBUG(D_V4L2, "Read exposure %d", *val);
+ return 0;
+}
+
+static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct hdcs *hdcs = sd->sensor_priv;
+ int rowexp, srowexp;
+ int max_srowexp;
+ /* Column time period */
+ int ct;
+ /* Column processing period */
+ int cp;
+ /* Row processing period */
+ int rp;
+ /* Minimum number of column timing periods
+ within the column processing period */
+ int mnct;
+ int cycles, err;
+ u8 exp[4];
+
+ cycles = val * HDCS_CLK_FREQ_MHZ;
+
+ ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2);
+ cp = hdcs->exp.cto + (hdcs->w * ct / 2);
+
+ /* the cycles one row takes */
+ rp = hdcs->exp.rs + cp;
+
+ rowexp = cycles / rp;
+
+ /* the remaining cycles */
+ cycles -= rowexp * rp;
+
+ /* calculate sub-row exposure */
+ if (IS_1020(sd)) {
+ /* see HDCS-1020 datasheet 3.5.6.4, p. 63 */
+ srowexp = hdcs->w - (cycles + hdcs->exp.er + 13) / ct;
+
+ mnct = (hdcs->exp.er + 12 + ct - 1) / ct;
+ max_srowexp = hdcs->w - mnct;
+ } else {
+ /* see HDCS-1000 datasheet 3.4.5.5, p. 61 */
+ srowexp = cp - hdcs->exp.er - 6 - cycles;
+
+ mnct = (hdcs->exp.er + 5 + ct - 1) / ct;
+ max_srowexp = cp - mnct * ct - 1;
+ }
+
+ if (srowexp < 0)
+ srowexp = 0;
+ else if (srowexp > max_srowexp)
+ srowexp = max_srowexp;
+
+ if (IS_1020(sd)) {
+ exp[0] = rowexp & 0xff;
+ exp[1] = rowexp >> 8;
+ exp[2] = (srowexp >> 2) & 0xff;
+ /* this clears exposure error flag */
+ exp[3] = 0x1;
+ err = hdcs_reg_write_seq(sd, HDCS_ROWEXPL, exp, 4);
+ } else {
+ exp[0] = rowexp & 0xff;
+ exp[1] = rowexp >> 8;
+ exp[2] = srowexp & 0xff;
+ exp[3] = srowexp >> 8;
+ err = hdcs_reg_write_seq(sd, HDCS_ROWEXPL, exp, 4);
+ if (err < 0)
+ return err;
+
+ /* clear exposure error flag */
+ err = stv06xx_write_sensor(sd,
+ HDCS_STATUS, BIT(4));
+ }
+ PDEBUG(D_V4L2, "Writing exposure %d, rowexp %d, srowexp %d",
+ val, rowexp, srowexp);
+ return err;
+}
+
+static int hdcs_set_gains(struct sd *sd, u8 r, u8 g, u8 b)
+{
+ u8 gains[4];
+
+ /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */
+ if (r > 127)
+ r = 0x80 | (r / 2);
+ if (g > 127)
+ g = 0x80 | (g / 2);
+ if (b > 127)
+ b = 0x80 | (b / 2);
+
+ gains[0] = g;
+ gains[1] = r;
+ gains[2] = b;
+ gains[3] = g;
+
+ return hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4);
+}
+
+static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+ u16 data;
+
+ err = stv06xx_read_sensor(sd, HDCS_ERECPGA, &data);
+
+ /* Bit 7 doubles the gain */
+ if (data & 0x80)
+ *val = (data & 0x7f) * 2;
+ else
+ *val = data;
+
+ PDEBUG(D_V4L2, "Read gain %d", *val);
+ return err;
+}
+
+static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ PDEBUG(D_V4L2, "Writing gain %d", val);
+ return hdcs_set_gains((struct sd *) gspca_dev,
+ val & 0xff, val & 0xff, val & 0xff);
+}
+
+static int hdcs_set_size(struct sd *sd,
+ unsigned int width, unsigned int height)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ u8 win[4];
+ unsigned int x, y;
+ int err;
+
+ /* must be multiple of 4 */
+ width = (width + 3) & ~0x3;
+ height = (height + 3) & ~0x3;
+
+ if (width > hdcs->array.width)
+ width = hdcs->array.width;
+
+ if (IS_1020(sd)) {
+ /* the borders are also invalid */
+ if (height + 2 * hdcs->array.border + HDCS_1020_BOTTOM_Y_SKIP
+ > hdcs->array.height)
+ height = hdcs->array.height - 2 * hdcs->array.border -
+ HDCS_1020_BOTTOM_Y_SKIP;
+
+ y = (hdcs->array.height - HDCS_1020_BOTTOM_Y_SKIP - height) / 2
+ + hdcs->array.top;
+ } else {
+ if (height > hdcs->array.height)
+ height = hdcs->array.height;
+
+ y = hdcs->array.top + (hdcs->array.height - height) / 2;
+ }
+
+ x = hdcs->array.left + (hdcs->array.width - width) / 2;
+
+ win[0] = y / 4;
+ win[1] = x / 4;
+ win[2] = (y + height) / 4 - 1;
+ win[3] = (x + width) / 4 - 1;
+
+ err = hdcs_reg_write_seq(sd, HDCS_FWROW, win, 4);
+ if (err < 0)
+ return err;
+
+ /* Update the current width and height */
+ hdcs->w = width;
+ hdcs->h = height;
+ return err;
+}
+
+static int hdcs_probe_1x00(struct sd *sd)
+{
+ struct hdcs *hdcs;
+ u16 sensor;
+ int ret;
+
+ ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor);
+ if (ret < 0 || sensor != 0x08)
+ return -ENODEV;
+
+ info("HDCS-1000/1100 sensor detected");
+
+ sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1x00.modes;
+ sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1x00.nmodes;
+ sd->desc.ctrls = stv06xx_sensor_hdcs1x00.ctrls;
+ sd->desc.nctrls = stv06xx_sensor_hdcs1x00.nctrls;
+
+ hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
+ if (!hdcs)
+ return -ENOMEM;
+
+ hdcs->array.left = 8;
+ hdcs->array.top = 8;
+ hdcs->array.width = HDCS_1X00_DEF_WIDTH;
+ hdcs->array.height = HDCS_1X00_DEF_HEIGHT;
+ hdcs->array.border = 4;
+
+ hdcs->exp.cto = 4;
+ hdcs->exp.cpo = 2;
+ hdcs->exp.rs = 186;
+ hdcs->exp.er = 100;
+
+ /*
+ * Frame rate on HDCS-1000 0x46D:0x840 depends on PSMP:
+ * 4 = doesn't work at all
+ * 5 = 7.8 fps,
+ * 6 = 6.9 fps,
+ * 8 = 6.3 fps,
+ * 10 = 5.5 fps,
+ * 15 = 4.4 fps,
+ * 31 = 2.8 fps
+ *
+ * Frame rate on HDCS-1000 0x46D:0x870 depends on PSMP:
+ * 15 = doesn't work at all
+ * 18 = doesn't work at all
+ * 19 = 7.3 fps
+ * 20 = 7.4 fps
+ * 21 = 7.4 fps
+ * 22 = 7.4 fps
+ * 24 = 6.3 fps
+ * 30 = 5.4 fps
+ */
+ hdcs->psmp = IS_870(sd) ? 20 : 5;
+
+ sd->sensor_priv = hdcs;
+
+ return 0;
+}
+
+static int hdcs_probe_1020(struct sd *sd)
+{
+ struct hdcs *hdcs;
+ u16 sensor;
+ int ret;
+
+ ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor);
+ if (ret < 0 || sensor != 0x10)
+ return -ENODEV;
+
+ info("HDCS-1020 sensor detected");
+
+ sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1020.modes;
+ sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1020.nmodes;
+ sd->desc.ctrls = stv06xx_sensor_hdcs1020.ctrls;
+ sd->desc.nctrls = stv06xx_sensor_hdcs1020.nctrls;
+
+ hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
+ if (!hdcs)
+ return -ENOMEM;
+
+ /*
+ * From Andrey's test image: looks like HDCS-1020 upper-left
+ * visible pixel is at 24,8 (y maybe even smaller?) and lower-right
+ * visible pixel at 375,299 (x maybe even larger?)
+ */
+ hdcs->array.left = 24;
+ hdcs->array.top = 4;
+ hdcs->array.width = HDCS_1020_DEF_WIDTH;
+ hdcs->array.height = 304;
+ hdcs->array.border = 4;
+
+ hdcs->psmp = 6;
+
+ hdcs->exp.cto = 3;
+ hdcs->exp.cpo = 3;
+ hdcs->exp.rs = 155;
+ hdcs->exp.er = 96;
+
+ sd->sensor_priv = hdcs;
+
+ return 0;
+}
+
+static int hdcs_start(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "Starting stream");
+
+ return hdcs_set_state(sd, HDCS_STATE_RUN);
+}
+
+static int hdcs_stop(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "Halting stream");
+
+ return hdcs_set_state(sd, HDCS_STATE_SLEEP);
+}
+
+static void hdcs_disconnect(struct sd *sd)
+{
+ PDEBUG(D_PROBE, "Disconnecting the sensor");
+ kfree(sd->sensor_priv);
+}
+
+static int hdcs_init(struct sd *sd)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ int i, err = 0;
+
+ /* Set the STV0602AA in STV0600 emulation mode */
+ if (IS_870(sd))
+ stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1);
+
+ /* Execute the bridge init */
+ for (i = 0; i < ARRAY_SIZE(stv_bridge_init) && !err; i++) {
+ err = stv06xx_write_bridge(sd, stv_bridge_init[i][0],
+ stv_bridge_init[i][1]);
+ }
+ if (err < 0)
+ return err;
+
+ /* sensor soft reset */
+ hdcs_reset(sd);
+
+ /* Execute the sensor init */
+ for (i = 0; i < ARRAY_SIZE(stv_sensor_init) && !err; i++) {
+ err = stv06xx_write_sensor(sd, stv_sensor_init[i][0],
+ stv_sensor_init[i][1]);
+ }
+ if (err < 0)
+ return err;
+
+ /* Enable continous frame capture, bit 2: stop when frame complete */
+ err = stv06xx_write_sensor(sd, HDCS_REG_CONFIG(sd), BIT(3));
+ if (err < 0)
+ return err;
+
+ /* Set PGA sample duration
+ (was 0x7E for IS_870, but caused slow framerate with HDCS-1020) */
+ if (IS_1020(sd))
+ err = stv06xx_write_sensor(sd, HDCS_TCTRL,
+ (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp);
+ else
+ err = stv06xx_write_sensor(sd, HDCS_TCTRL,
+ (HDCS_ADC_START_SIG_DUR << 5) | hdcs->psmp);
+ if (err < 0)
+ return err;
+
+ err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN, HDCS_DEFAULT_GAIN,
+ HDCS_DEFAULT_GAIN);
+ if (err < 0)
+ return err;
+
+ err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE);
+ if (err < 0)
+ return err;
+
+ err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height);
+ return err;
+}
+
+static int hdcs_dump(struct sd *sd)
+{
+ u16 reg, val;
+
+ info("Dumping sensor registers:");
+
+ for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) {
+ stv06xx_read_sensor(sd, reg, &val);
+ info("reg 0x%02x = 0x%02x", reg, val);
+ }
+ return 0;
+}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
new file mode 100644
index 000000000000..9c7279a4cd88
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2008 Chia-I Wu
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_HDCS_H_
+#define STV06XX_HDCS_H_
+
+#include "stv06xx_sensor.h"
+
+#define HDCS_REG_CONFIG(sd) (IS_1020(sd) ? HDCS20_CONFIG : HDCS00_CONFIG)
+#define HDCS_REG_CONTROL(sd) (IS_1020(sd) ? HDCS20_CONTROL : HDCS00_CONTROL)
+
+#define HDCS_1X00_DEF_WIDTH 360
+#define HDCS_1X00_DEF_HEIGHT 296
+
+#define HDCS_1020_DEF_WIDTH 352
+#define HDCS_1020_DEF_HEIGHT 292
+
+#define HDCS_1020_BOTTOM_Y_SKIP 4
+
+#define HDCS_CLK_FREQ_MHZ 25
+
+#define HDCS_ADC_START_SIG_DUR 3
+
+/* LSB bit of I2C or register address signifies write (0) or read (1) */
+/* I2C Registers common for both HDCS-1000/1100 and HDCS-1020 */
+/* Identifications Register */
+#define HDCS_IDENT (0x00 << 1)
+/* Status Register */
+#define HDCS_STATUS (0x01 << 1)
+/* Interrupt Mask Register */
+#define HDCS_IMASK (0x02 << 1)
+/* Pad Control Register */
+#define HDCS_PCTRL (0x03 << 1)
+/* Pad Drive Control Register */
+#define HDCS_PDRV (0x04 << 1)
+/* Interface Control Register */
+#define HDCS_ICTRL (0x05 << 1)
+/* Interface Timing Register */
+#define HDCS_ITMG (0x06 << 1)
+/* Baud Fraction Register */
+#define HDCS_BFRAC (0x07 << 1)
+/* Baud Rate Register */
+#define HDCS_BRATE (0x08 << 1)
+/* ADC Control Register */
+#define HDCS_ADCCTRL (0x09 << 1)
+/* First Window Row Register */
+#define HDCS_FWROW (0x0a << 1)
+/* First Window Column Register */
+#define HDCS_FWCOL (0x0b << 1)
+/* Last Window Row Register */
+#define HDCS_LWROW (0x0c << 1)
+/* Last Window Column Register */
+#define HDCS_LWCOL (0x0d << 1)
+/* Timing Control Register */
+#define HDCS_TCTRL (0x0e << 1)
+/* PGA Gain Register: Even Row, Even Column */
+#define HDCS_ERECPGA (0x0f << 1)
+/* PGA Gain Register: Even Row, Odd Column */
+#define HDCS_EROCPGA (0x10 << 1)
+/* PGA Gain Register: Odd Row, Even Column */
+#define HDCS_ORECPGA (0x11 << 1)
+/* PGA Gain Register: Odd Row, Odd Column */
+#define HDCS_OROCPGA (0x12 << 1)
+/* Row Exposure Low Register */
+#define HDCS_ROWEXPL (0x13 << 1)
+/* Row Exposure High Register */
+#define HDCS_ROWEXPH (0x14 << 1)
+
+/* I2C Registers only for HDCS-1000/1100 */
+/* Sub-Row Exposure Low Register */
+#define HDCS00_SROWEXPL (0x15 << 1)
+/* Sub-Row Exposure High Register */
+#define HDCS00_SROWEXPH (0x16 << 1)
+/* Configuration Register */
+#define HDCS00_CONFIG (0x17 << 1)
+/* Control Register */
+#define HDCS00_CONTROL (0x18 << 1)
+
+/* I2C Registers only for HDCS-1020 */
+/* Sub-Row Exposure Register */
+#define HDCS20_SROWEXP (0x15 << 1)
+/* Error Control Register */
+#define HDCS20_ERROR (0x16 << 1)
+/* Interface Timing 2 Register */
+#define HDCS20_ITMG2 (0x17 << 1)
+/* Interface Control 2 Register */
+#define HDCS20_ICTRL2 (0x18 << 1)
+/* Horizontal Blank Register */
+#define HDCS20_HBLANK (0x19 << 1)
+/* Vertical Blank Register */
+#define HDCS20_VBLANK (0x1a << 1)
+/* Configuration Register */
+#define HDCS20_CONFIG (0x1b << 1)
+/* Control Register */
+#define HDCS20_CONTROL (0x1c << 1)
+
+#define HDCS_RUN_ENABLE (1 << 2)
+#define HDCS_SLEEP_MODE (1 << 1)
+
+#define HDCS_DEFAULT_EXPOSURE 5000
+#define HDCS_DEFAULT_GAIN 128
+
+static int hdcs_probe_1x00(struct sd *sd);
+static int hdcs_probe_1020(struct sd *sd);
+static int hdcs_start(struct sd *sd);
+static int hdcs_init(struct sd *sd);
+static int hdcs_stop(struct sd *sd);
+static int hdcs_dump(struct sd *sd);
+static void hdcs_disconnect(struct sd *sd);
+
+static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+
+const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = {
+ .name = "HP HDCS-1000/1100",
+ .i2c_flush = 0,
+ .i2c_addr = (0x55 << 1),
+ .i2c_len = 1,
+
+ .init = hdcs_init,
+ .probe = hdcs_probe_1x00,
+ .start = hdcs_start,
+ .stop = hdcs_stop,
+ .disconnect = hdcs_disconnect,
+ .dump = hdcs_dump,
+
+ .nctrls = 2,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0xffff,
+ .step = 0x1,
+ .default_value = HDCS_DEFAULT_EXPOSURE,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = hdcs_set_exposure,
+ .get = hdcs_get_exposure
+ },
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = HDCS_DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = hdcs_set_gain,
+ .get = hdcs_get_gain
+ }
+ },
+
+ .nmodes = 1,
+ .modes = {
+ {
+ HDCS_1X00_DEF_WIDTH,
+ HDCS_1X00_DEF_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT,
+ .bytesperline = HDCS_1X00_DEF_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = {
+ .name = "HDCS-1020",
+ .i2c_flush = 0,
+ .i2c_addr = (0x55 << 1),
+ .i2c_len = 1,
+
+ .nctrls = 0,
+ .ctrls = {},
+
+ .init = hdcs_init,
+ .probe = hdcs_probe_1020,
+ .start = hdcs_start,
+ .stop = hdcs_stop,
+ .dump = hdcs_dump,
+
+ .nmodes = 1,
+ .modes = {
+ {
+ HDCS_1020_DEF_WIDTH,
+ HDCS_1020_DEF_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT,
+ .bytesperline = HDCS_1020_DEF_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+static const u16 stv_bridge_init[][2] = {
+ {STV_ISO_ENABLE, 0},
+ {STV_REG23, 0},
+ {STV_REG00, 0x1d},
+ {STV_REG01, 0xb5},
+ {STV_REG02, 0xa8},
+ {STV_REG03, 0x95},
+ {STV_REG04, 0x07},
+
+ {STV_SCAN_RATE, 0x20},
+ {STV_ISO_SIZE_L, 847},
+ {STV_Y_CTRL, 0x01},
+ {STV_X_CTRL, 0x0a}
+};
+
+static const u8 stv_sensor_init[][2] = {
+ /* Clear status (writing 1 will clear the corresponding status bit) */
+ {HDCS_STATUS, BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
+ /* Disable all interrupts */
+ {HDCS_IMASK, 0x00},
+ {HDCS_PCTRL, BIT(6) | BIT(5) | BIT(1) | BIT(0)},
+ {HDCS_PDRV, 0x00},
+ {HDCS_ICTRL, BIT(5)},
+ {HDCS_ITMG, BIT(4) | BIT(1)},
+ /* ADC output resolution to 10 bits */
+ {HDCS_ADCCTRL, 10}
+};
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
new file mode 100644
index 000000000000..d0a0f8596454
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+/*
+ * The spec file for the PB-0100 suggests the following for best quality
+ * images after the sensor has been reset :
+ *
+ * PB_ADCGAINL = R60 = 0x03 (3 dec) : sets low reference of ADC
+ to produce good black level
+ * PB_PREADCTRL = R32 = 0x1400 (5120 dec) : Enables global gain changes
+ through R53
+ * PB_ADCMINGAIN = R52 = 0x10 (16 dec) : Sets the minimum gain for
+ auto-exposure
+ * PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec) : Sets the global gain
+ * PB_EXPGAIN = R14 = 0x11 (17 dec) : Sets the auto-exposure value
+ * PB_UPDATEINT = R23 = 0x02 (2 dec) : Sets the speed on
+ auto-exposure routine
+ * PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate
+ */
+
+#include "stv06xx_pb0100.h"
+
+static int pb0100_probe(struct sd *sd)
+{
+ u16 sensor;
+ int i, err;
+ s32 *sensor_settings;
+
+ err = stv06xx_read_sensor(sd, PB_IDENT, &sensor);
+
+ if (err < 0)
+ return -ENODEV;
+
+ if ((sensor >> 8) == 0x64) {
+ sensor_settings = kmalloc(
+ stv06xx_sensor_pb0100.nctrls * sizeof(s32),
+ GFP_KERNEL);
+ if (!sensor_settings)
+ return -ENOMEM;
+
+ info("Photobit pb0100 sensor detected");
+
+ sd->gspca_dev.cam.cam_mode = stv06xx_sensor_pb0100.modes;
+ sd->gspca_dev.cam.nmodes = stv06xx_sensor_pb0100.nmodes;
+ sd->desc.ctrls = stv06xx_sensor_pb0100.ctrls;
+ sd->desc.nctrls = stv06xx_sensor_pb0100.nctrls;
+ for (i = 0; i < stv06xx_sensor_pb0100.nctrls; i++)
+ sensor_settings[i] = stv06xx_sensor_pb0100.
+ ctrls[i].qctrl.default_value;
+ sd->sensor_priv = sensor_settings;
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int pb0100_start(struct sd *sd)
+{
+ int err;
+ struct cam *cam = &sd->gspca_dev.cam;
+ s32 *sensor_settings = sd->sensor_priv;
+ u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+
+ /* Setup sensor window */
+ if (mode & PB0100_CROP_TO_VGA) {
+ stv06xx_write_sensor(sd, PB_RSTART, 30);
+ stv06xx_write_sensor(sd, PB_CSTART, 20);
+ stv06xx_write_sensor(sd, PB_RWSIZE, 240 - 1);
+ stv06xx_write_sensor(sd, PB_CWSIZE, 320 - 1);
+ } else {
+ stv06xx_write_sensor(sd, PB_RSTART, 8);
+ stv06xx_write_sensor(sd, PB_CSTART, 4);
+ stv06xx_write_sensor(sd, PB_RWSIZE, 288 - 1);
+ stv06xx_write_sensor(sd, PB_CWSIZE, 352 - 1);
+ }
+
+ if (mode & PB0100_SUBSAMPLE) {
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); /* Wrong, FIXME */
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
+
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
+ } else {
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
+ /* larger -> slower */
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);
+ }
+
+ /* set_gain also sets red and blue balance */
+ pb0100_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
+ pb0100_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]);
+ pb0100_set_autogain_target(&sd->gspca_dev,
+ sensor_settings[AUTOGAIN_TARGET_IDX]);
+ pb0100_set_autogain(&sd->gspca_dev, sensor_settings[AUTOGAIN_IDX]);
+
+ err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1));
+ PDEBUG(D_STREAM, "Started stream, status: %d", err);
+
+ return (err < 0) ? err : 0;
+}
+
+static int pb0100_stop(struct sd *sd)
+{
+ int err;
+
+ err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1);
+
+ if (err < 0)
+ goto out;
+
+ /* Set bit 1 to zero */
+ err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));
+
+ PDEBUG(D_STREAM, "Halting stream");
+out:
+ return (err < 0) ? err : 0;
+}
+
+/* FIXME: Sort the init commands out and put them into tables,
+ this is only for getting the camera to work */
+/* FIXME: No error handling for now,
+ add this once the init has been converted to proper tables */
+static int pb0100_init(struct sd *sd)
+{
+ stv06xx_write_bridge(sd, STV_REG00, 1);
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0);
+
+ /* Reset sensor */
+ stv06xx_write_sensor(sd, PB_RESET, 1);
+ stv06xx_write_sensor(sd, PB_RESET, 0);
+
+ /* Disable chip */
+ stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));
+
+ /* Gain stuff...*/
+ stv06xx_write_sensor(sd, PB_PREADCTRL, BIT(12)|BIT(10)|BIT(6));
+ stv06xx_write_sensor(sd, PB_ADCGLOBALGAIN, 12);
+
+ /* Set up auto-exposure */
+ /* ADC VREF_HI new setting for a transition
+ from the Expose1 to the Expose2 setting */
+ stv06xx_write_sensor(sd, PB_R28, 12);
+ /* gain max for autoexposure */
+ stv06xx_write_sensor(sd, PB_ADCMAXGAIN, 180);
+ /* gain min for autoexposure */
+ stv06xx_write_sensor(sd, PB_ADCMINGAIN, 12);
+ /* Maximum frame integration time (programmed into R8)
+ allowed for auto-exposure routine */
+ stv06xx_write_sensor(sd, PB_R54, 3);
+ /* Minimum frame integration time (programmed into R8)
+ allowed for auto-exposure routine */
+ stv06xx_write_sensor(sd, PB_R55, 0);
+ stv06xx_write_sensor(sd, PB_UPDATEINT, 1);
+ /* R15 Expose0 (maximum that auto-exposure may use) */
+ stv06xx_write_sensor(sd, PB_R15, 800);
+ /* R17 Expose2 (minimum that auto-exposure may use) */
+ stv06xx_write_sensor(sd, PB_R17, 10);
+
+ stv06xx_write_sensor(sd, PB_EXPGAIN, 0);
+
+ /* 0x14 */
+ stv06xx_write_sensor(sd, PB_VOFFSET, 0);
+ /* 0x0D */
+ stv06xx_write_sensor(sd, PB_ADCGAINH, 11);
+ /* Set black level (important!) */
+ stv06xx_write_sensor(sd, PB_ADCGAINL, 0);
+
+ /* ??? */
+ stv06xx_write_bridge(sd, STV_REG00, 0x11);
+ stv06xx_write_bridge(sd, STV_REG03, 0x45);
+ stv06xx_write_bridge(sd, STV_REG04, 0x07);
+
+ /* ISO-Size (0x27b: 635... why? - HDCS uses 847) */
+ stv06xx_write_bridge(sd, STV_ISO_SIZE_L, 847);
+
+ /* Scan/timing for the sensor */
+ stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1));
+ stv06xx_write_sensor(sd, PB_CFILLIN, 14);
+ stv06xx_write_sensor(sd, PB_VBL, 0);
+ stv06xx_write_sensor(sd, PB_FINTTIME, 0);
+ stv06xx_write_sensor(sd, PB_RINTTIME, 123);
+
+ stv06xx_write_bridge(sd, STV_REG01, 0xc2);
+ stv06xx_write_bridge(sd, STV_REG02, 0xb0);
+ return 0;
+}
+
+static int pb0100_dump(struct sd *sd)
+{
+ return 0;
+}
+
+static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[GAIN_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[GAIN_IDX] = val;
+ err = stv06xx_write_sensor(sd, PB_G1GAIN, val);
+ if (!err)
+ err = stv06xx_write_sensor(sd, PB_G2GAIN, val);
+ PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err);
+
+ if (!err)
+ err = pb0100_set_red_balance(gspca_dev,
+ sensor_settings[RED_BALANCE_IDX]);
+ if (!err)
+ err = pb0100_set_blue_balance(gspca_dev,
+ sensor_settings[BLUE_BALANCE_IDX]);
+
+ return err;
+}
+
+static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[RED_BALANCE_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[RED_BALANCE_IDX] = val;
+ val += sensor_settings[GAIN_IDX];
+ if (val < 0)
+ val = 0;
+ else if (val > 255)
+ val = 255;
+
+ err = stv06xx_write_sensor(sd, PB_RGAIN, val);
+ PDEBUG(D_V4L2, "Set red gain to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[BLUE_BALANCE_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[BLUE_BALANCE_IDX] = val;
+ val += sensor_settings[GAIN_IDX];
+ if (val < 0)
+ val = 0;
+ else if (val > 255)
+ val = 255;
+
+ err = stv06xx_write_sensor(sd, PB_BGAIN, val);
+ PDEBUG(D_V4L2, "Set blue gain to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[EXPOSURE_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[EXPOSURE_IDX] = val;
+ err = stv06xx_write_sensor(sd, PB_RINTTIME, val);
+ PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTOGAIN_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[AUTOGAIN_IDX] = val;
+ if (sensor_settings[AUTOGAIN_IDX]) {
+ if (sensor_settings[NATURAL_IDX])
+ val = BIT(6)|BIT(4)|BIT(0);
+ else
+ val = BIT(4)|BIT(0);
+ } else
+ val = 0;
+
+ err = stv06xx_write_sensor(sd, PB_EXPGAIN, val);
+ PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d",
+ sensor_settings[AUTOGAIN_IDX], sensor_settings[NATURAL_IDX],
+ err);
+
+ return err;
+}
+
+static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTOGAIN_TARGET_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err, totalpixels, brightpixels, darkpixels;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[AUTOGAIN_TARGET_IDX] = val;
+
+ /* Number of pixels counted by the sensor when subsampling the pixels.
+ * Slightly larger than the real value to avoid oscillation */
+ totalpixels = gspca_dev->width * gspca_dev->height;
+ totalpixels = totalpixels/(8*8) + totalpixels/(64*64);
+
+ brightpixels = (totalpixels * val) >> 8;
+ darkpixels = totalpixels - brightpixels;
+ err = stv06xx_write_sensor(sd, PB_R21, brightpixels);
+ if (!err)
+ err = stv06xx_write_sensor(sd, PB_R22, darkpixels);
+
+ PDEBUG(D_V4L2, "Set autogain target to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[NATURAL_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[NATURAL_IDX] = val;
+
+ return pb0100_set_autogain(gspca_dev, sensor_settings[AUTOGAIN_IDX]);
+}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
new file mode 100644
index 000000000000..5ea21a1154c4
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_PB0100_H_
+#define STV06XX_PB0100_H_
+
+#include "stv06xx_sensor.h"
+
+/* mode priv field flags */
+#define PB0100_CROP_TO_VGA 0x01
+#define PB0100_SUBSAMPLE 0x02
+
+/* I2C Registers */
+#define PB_IDENT 0x00 /* Chip Version */
+#define PB_RSTART 0x01 /* Row Window Start */
+#define PB_CSTART 0x02 /* Column Window Start */
+#define PB_RWSIZE 0x03 /* Row Window Size */
+#define PB_CWSIZE 0x04 /* Column Window Size */
+#define PB_CFILLIN 0x05 /* Column Fill-In */
+#define PB_VBL 0x06 /* Vertical Blank Count */
+#define PB_CONTROL 0x07 /* Control Mode */
+#define PB_FINTTIME 0x08 /* Integration Time/Frame Unit Count */
+#define PB_RINTTIME 0x09 /* Integration Time/Row Unit Count */
+#define PB_ROWSPEED 0x0a /* Row Speed Control */
+#define PB_ABORTFRAME 0x0b /* Abort Frame */
+#define PB_R12 0x0c /* Reserved */
+#define PB_RESET 0x0d /* Reset */
+#define PB_EXPGAIN 0x0e /* Exposure Gain Command */
+#define PB_R15 0x0f /* Expose0 */
+#define PB_R16 0x10 /* Expose1 */
+#define PB_R17 0x11 /* Expose2 */
+#define PB_R18 0x12 /* Low0_DAC */
+#define PB_R19 0x13 /* Low1_DAC */
+#define PB_R20 0x14 /* Low2_DAC */
+#define PB_R21 0x15 /* Threshold11 */
+#define PB_R22 0x16 /* Threshold0x */
+#define PB_UPDATEINT 0x17 /* Update Interval */
+#define PB_R24 0x18 /* High_DAC */
+#define PB_R25 0x19 /* Trans0H */
+#define PB_R26 0x1a /* Trans1L */
+#define PB_R27 0x1b /* Trans1H */
+#define PB_R28 0x1c /* Trans2L */
+#define PB_R29 0x1d /* Reserved */
+#define PB_R30 0x1e /* Reserved */
+#define PB_R31 0x1f /* Wait to Read */
+#define PB_PREADCTRL 0x20 /* Pixel Read Control Mode */
+#define PB_R33 0x21 /* IREF_VLN */
+#define PB_R34 0x22 /* IREF_VLP */
+#define PB_R35 0x23 /* IREF_VLN_INTEG */
+#define PB_R36 0x24 /* IREF_MASTER */
+#define PB_R37 0x25 /* IDACP */
+#define PB_R38 0x26 /* IDACN */
+#define PB_R39 0x27 /* DAC_Control_Reg */
+#define PB_R40 0x28 /* VCL */
+#define PB_R41 0x29 /* IREF_VLN_ADCIN */
+#define PB_R42 0x2a /* Reserved */
+#define PB_G1GAIN 0x2b /* Green 1 Gain */
+#define PB_BGAIN 0x2c /* Blue Gain */
+#define PB_RGAIN 0x2d /* Red Gain */
+#define PB_G2GAIN 0x2e /* Green 2 Gain */
+#define PB_R47 0x2f /* Dark Row Address */
+#define PB_R48 0x30 /* Dark Row Options */
+#define PB_R49 0x31 /* Reserved */
+#define PB_R50 0x32 /* Image Test Data */
+#define PB_ADCMAXGAIN 0x33 /* Maximum Gain */
+#define PB_ADCMINGAIN 0x34 /* Minimum Gain */
+#define PB_ADCGLOBALGAIN 0x35 /* Global Gain */
+#define PB_R54 0x36 /* Maximum Frame */
+#define PB_R55 0x37 /* Minimum Frame */
+#define PB_R56 0x38 /* Reserved */
+#define PB_VOFFSET 0x39 /* VOFFSET */
+#define PB_R58 0x3a /* Snap-Shot Sequence Trigger */
+#define PB_ADCGAINH 0x3b /* VREF_HI */
+#define PB_ADCGAINL 0x3c /* VREF_LO */
+#define PB_R61 0x3d /* Reserved */
+#define PB_R62 0x3e /* Reserved */
+#define PB_R63 0x3f /* Reserved */
+#define PB_R64 0x40 /* Red/Blue Gain */
+#define PB_R65 0x41 /* Green 2/Green 1 Gain */
+#define PB_R66 0x42 /* VREF_HI/LO */
+#define PB_R67 0x43 /* Integration Time/Row Unit Count */
+#define PB_R240 0xf0 /* ADC Test */
+#define PB_R241 0xf1 /* Chip Enable */
+#define PB_R242 0xf2 /* Reserved */
+
+static int pb0100_probe(struct sd *sd);
+static int pb0100_start(struct sd *sd);
+static int pb0100_init(struct sd *sd);
+static int pb0100_stop(struct sd *sd);
+static int pb0100_dump(struct sd *sd);
+
+/* V4L2 controls supported by the driver */
+static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val);
+
+const struct stv06xx_sensor stv06xx_sensor_pb0100 = {
+ .name = "PB-0100",
+ .i2c_flush = 1,
+ .i2c_addr = 0xba,
+ .i2c_len = 2,
+
+ .nctrls = 7,
+ .ctrls = {
+#define GAIN_IDX 0
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128
+ },
+ .set = pb0100_set_gain,
+ .get = pb0100_get_gain
+ },
+#define RED_BALANCE_IDX 1
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = -255,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = pb0100_set_red_balance,
+ .get = pb0100_get_red_balance
+ },
+#define BLUE_BALANCE_IDX 2
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = -255,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = pb0100_set_blue_balance,
+ .get = pb0100_get_blue_balance
+ },
+#define EXPOSURE_IDX 3
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 511,
+ .step = 1,
+ .default_value = 12
+ },
+ .set = pb0100_set_exposure,
+ .get = pb0100_get_exposure
+ },
+#define AUTOGAIN_IDX 4
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Gain and Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = pb0100_set_autogain,
+ .get = pb0100_get_autogain
+ },
+#define AUTOGAIN_TARGET_IDX 5
+ {
+ {
+ .id = V4L2_CTRL_CLASS_USER + 0x1000,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Automatic Gain Target",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128
+ },
+ .set = pb0100_set_autogain_target,
+ .get = pb0100_get_autogain_target
+ },
+#define NATURAL_IDX 6
+ {
+ {
+ .id = V4L2_CTRL_CLASS_USER + 0x1001,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Natural Light Source",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = pb0100_set_natural,
+ .get = pb0100_get_natural
+ },
+ },
+
+ .init = pb0100_init,
+ .probe = pb0100_probe,
+ .start = pb0100_start,
+ .stop = pb0100_stop,
+ .dump = pb0100_dump,
+
+ .nmodes = 2,
+ .modes = {
+/* low res / subsample modes disabled as they are only half res horizontal,
+ halving the vertical resolution does not seem to work */
+ {
+ 320,
+ 240,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 320 * 240,
+ .bytesperline = 320,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = PB0100_CROP_TO_VGA
+ },
+ {
+ 352,
+ 288,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 352 * 288,
+ .bytesperline = 352,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ },
+ }
+};
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
new file mode 100644
index 000000000000..c726dacefa1f
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_SENSOR_H_
+#define STV06XX_SENSOR_H_
+
+#include "stv06xx.h"
+
+#define IS_850(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x850)
+#define IS_870(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x870)
+#define IS_1020(sd) ((sd)->sensor == &stv06xx_sensor_hdcs1020)
+
+extern const struct stv06xx_sensor stv06xx_sensor_vv6410;
+extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00;
+extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020;
+extern const struct stv06xx_sensor stv06xx_sensor_pb0100;
+
+#define STV06XX_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10)
+
+struct stv06xx_sensor {
+ /* Defines the name of a sensor */
+ char name[32];
+
+ /* Sensor i2c address */
+ u8 i2c_addr;
+
+ /* Flush value*/
+ u8 i2c_flush;
+
+ /* length of an i2c word */
+ u8 i2c_len;
+
+ /* Probes if the sensor is connected */
+ int (*probe)(struct sd *sd);
+
+ /* Performs a initialization sequence */
+ int (*init)(struct sd *sd);
+
+ /* Executed at device disconnect */
+ void (*disconnect)(struct sd *sd);
+
+ /* Reads a sensor register */
+ int (*read_sensor)(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+ /* Writes to a sensor register */
+ int (*write_sensor)(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+ /* Instructs the sensor to start streaming */
+ int (*start)(struct sd *sd);
+
+ /* Instructs the sensor to stop streaming */
+ int (*stop)(struct sd *sd);
+
+ /* Instructs the sensor to dump all its contents */
+ int (*dump)(struct sd *sd);
+
+ int nctrls;
+ struct ctrl ctrls[STV06XX_MAX_CTRLS];
+
+ char nmodes;
+ struct v4l2_pix_format modes[];
+};
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
new file mode 100644
index 000000000000..1ca91f2a6dee
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#include "stv06xx_vv6410.h"
+
+static int vv6410_probe(struct sd *sd)
+{
+ u16 data;
+ int err;
+
+ err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data);
+
+ if (err < 0)
+ return -ENODEV;
+
+ if (data == 0x19) {
+ info("vv6410 sensor detected");
+
+ sd->gspca_dev.cam.cam_mode = stv06xx_sensor_vv6410.modes;
+ sd->gspca_dev.cam.nmodes = stv06xx_sensor_vv6410.nmodes;
+ sd->desc.ctrls = stv06xx_sensor_vv6410.ctrls;
+ sd->desc.nctrls = stv06xx_sensor_vv6410.nctrls;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int vv6410_init(struct sd *sd)
+{
+ int err = 0, i;
+
+ for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) {
+ /* if NULL then len contains single value */
+ if (stv_bridge_init[i].data == NULL) {
+ err = stv06xx_write_bridge(sd,
+ stv_bridge_init[i].start,
+ stv_bridge_init[i].len);
+ } else {
+ int j;
+ for (j = 0; j < stv_bridge_init[i].len; j++)
+ err = stv06xx_write_bridge(sd,
+ stv_bridge_init[i].start + j,
+ stv_bridge_init[i].data[j]);
+ }
+ }
+
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init,
+ ARRAY_SIZE(vv6410_sensor_init));
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_start(struct sd *sd)
+{
+ int err;
+ struct cam *cam = &sd->gspca_dev.cam;
+ u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+
+ if (priv & VV6410_CROP_TO_QVGA) {
+ PDEBUG(D_CONF, "Cropping to QVGA");
+ stv06xx_write_sensor(sd, VV6410_XENDH, 320 - 1);
+ stv06xx_write_sensor(sd, VV6410_YENDH, 240 - 1);
+ } else {
+ stv06xx_write_sensor(sd, VV6410_XENDH, 360 - 1);
+ stv06xx_write_sensor(sd, VV6410_YENDH, 294 - 1);
+ }
+
+ if (priv & VV6410_SUBSAMPLE) {
+ PDEBUG(D_CONF, "Enabling subsampling");
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02);
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
+
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
+ } else {
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
+
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);
+ }
+
+ /* Turn on LED */
+ err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0);
+ if (err < 0)
+ return err;
+
+ PDEBUG(D_STREAM, "Starting stream");
+
+ return 0;
+}
+
+static int vv6410_stop(struct sd *sd)
+{
+ int err;
+
+ /* Turn off LED */
+ err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE);
+ if (err < 0)
+ return err;
+
+ PDEBUG(D_STREAM, "Halting stream");
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_dump(struct sd *sd)
+{
+ u8 i;
+ int err = 0;
+
+ info("Dumping all vv6410 sensor registers");
+ for (i = 0; i < 0xff && !err; i++) {
+ u16 data;
+ err = stv06xx_read_sensor(sd, i, &data);
+ info("Register 0x%x contained 0x%x", i, data);
+ }
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+
+ *val = (i2c_data & VV6410_HFLIP) ? 1 : 0;
+
+ PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+ if (err < 0)
+ return err;
+
+ if (val)
+ i2c_data |= VV6410_HFLIP;
+ else
+ i2c_data &= ~VV6410_HFLIP;
+
+ PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+
+ *val = (i2c_data & VV6410_VFLIP) ? 1 : 0;
+
+ PDEBUG(D_V4L2, "Read vertical flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+ if (err < 0)
+ return err;
+
+ if (val)
+ i2c_data |= VV6410_VFLIP;
+ else
+ i2c_data &= ~VV6410_VFLIP;
+
+ PDEBUG(D_V4L2, "Set vertical flip to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = stv06xx_read_sensor(sd, VV6410_ANALOGGAIN, &i2c_data);
+
+ *val = i2c_data & 0xf;
+
+ PDEBUG(D_V4L2, "Read analog gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(D_V4L2, "Set analog gain to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf));
+
+ return (err < 0) ? err : 0;
+}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
new file mode 100644
index 000000000000..3ff8c4ea3362
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_VV6410_H_
+#define STV06XX_VV6410_H_
+
+#include "stv06xx_sensor.h"
+
+#define VV6410_COLS 416
+#define VV6410_ROWS 320
+
+/* Status registers */
+/* Chip identification number including revision indicator */
+#define VV6410_DEVICEH 0x00
+#define VV6410_DEVICEL 0x01
+
+/* User can determine whether timed I2C data
+ has been consumed by interrogating flag states */
+#define VV6410_STATUS0 0x02
+
+/* Current line counter value */
+#define VV6410_LINECOUNTH 0x03
+#define VV6410_LINECOUNTL 0x04
+
+/* End x coordinate of image size */
+#define VV6410_XENDH 0x05
+#define VV6410_XENDL 0x06
+
+/* End y coordinate of image size */
+#define VV6410_YENDH 0x07
+#define VV6410_YENDL 0x08
+
+/* This is the average pixel value returned from the
+ dark line offset cancellation algorithm */
+#define VV6410_DARKAVGH 0x09
+#define VV6410_DARKAVGL 0x0a
+
+/* This is the average pixel value returned from the
+ black line offset cancellation algorithm */
+#define VV6410_BLACKAVGH 0x0b
+#define VV6410_BLACKAVGL 0x0c
+
+/* Flags to indicate whether the x or y image coordinates have been clipped */
+#define VV6410_STATUS1 0x0d
+
+/* Setup registers */
+
+/* Low-power/sleep modes & video timing */
+#define VV6410_SETUP0 0x10
+
+/* Various parameters */
+#define VV6410_SETUP1 0x11
+
+/* Contains pixel counter reset value used by external sync */
+#define VV6410_SYNCVALUE 0x12
+
+/* Frame grabbing modes (FST, LST and QCK) */
+#define VV6410_FGMODES 0x14
+
+/* FST and QCK mapping modes. */
+#define VV6410_PINMAPPING 0x15
+
+/* Data resolution */
+#define VV6410_DATAFORMAT 0x16
+
+/* Output coding formats */
+#define VV6410_OPFORMAT 0x17
+
+/* Various mode select bits */
+#define VV6410_MODESELECT 0x18
+
+/* Exposure registers */
+/* Fine exposure. */
+#define VV6410_FINEH 0x20
+#define VV6410_FINEL 0x21
+
+/* Coarse exposure */
+#define VV6410_COARSEH 0x22
+#define VV6410_COARSEL 0x23
+
+/* Analog gain setting */
+#define VV6410_ANALOGGAIN 0x24
+
+/* Clock division */
+#define VV6410_CLKDIV 0x25
+
+/* Dark line offset cancellation value */
+#define VV6410_DARKOFFSETH 0x2c
+#define VV6410_DARKOFFSETL 0x2d
+
+/* Dark line offset cancellation enable */
+#define VV6410_DARKOFFSETSETUP 0x2e
+
+/* Video timing registers */
+/* Line Length (Pixel Clocks) */
+#define VV6410_LINELENGTHH 0x52
+#define VV6410_LINELENGTHL 0x53
+
+/* X-co-ordinate of top left corner of region of interest (x-offset) */
+#define VV6410_XOFFSETH 0x57
+#define VV6410_XOFFSETL 0x58
+
+/* Y-coordinate of top left corner of region of interest (y-offset) */
+#define VV6410_YOFFSETH 0x59
+#define VV6410_YOFFSETL 0x5a
+
+/* Field length (Lines) */
+#define VV6410_FIELDLENGTHH 0x61
+#define VV6410_FIELDLENGTHL 0x62
+
+/* System registers */
+/* Black offset cancellation default value */
+#define VV6410_BLACKOFFSETH 0x70
+#define VV6410_BLACKOFFSETL 0x71
+
+/* Black offset cancellation setup */
+#define VV6410_BLACKOFFSETSETUP 0x72
+
+/* Analog Control Register 0 */
+#define VV6410_CR0 0x75
+
+/* Analog Control Register 1 */
+#define VV6410_CR1 0x76
+
+/* ADC Setup Register */
+#define VV6410_AS0 0x77
+
+/* Analog Test Register */
+#define VV6410_AT0 0x78
+
+/* Audio Amplifier Setup Register */
+#define VV6410_AT1 0x79
+
+#define VV6410_HFLIP (1 << 3)
+#define VV6410_VFLIP (1 << 4)
+
+#define VV6410_LOW_POWER_MODE (1 << 0)
+#define VV6410_SOFT_RESET (1 << 2)
+#define VV6410_PAL_25_FPS (0 << 3)
+
+#define VV6410_CLK_DIV_2 (1 << 1)
+
+#define VV6410_FINE_EXPOSURE 320
+#define VV6410_COARSE_EXPOSURE 192
+#define VV6410_DEFAULT_GAIN 5
+
+#define VV6410_SUBSAMPLE 0x01
+#define VV6410_CROP_TO_QVGA 0x02
+
+static int vv6410_probe(struct sd *sd);
+static int vv6410_start(struct sd *sd);
+static int vv6410_init(struct sd *sd);
+static int vv6410_stop(struct sd *sd);
+static int vv6410_dump(struct sd *sd);
+
+/* V4L2 controls supported by the driver */
+static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val);
+
+const struct stv06xx_sensor stv06xx_sensor_vv6410 = {
+ .name = "ST VV6410",
+ .i2c_flush = 5,
+ .i2c_addr = 0x20,
+ .i2c_len = 1,
+ .init = vv6410_init,
+ .probe = vv6410_probe,
+ .start = vv6410_start,
+ .stop = vv6410_stop,
+ .dump = vv6410_dump,
+
+ .nctrls = 3,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = vv6410_set_hflip,
+ .get = vv6410_get_hflip
+ }, {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = vv6410_set_vflip,
+ .get = vv6410_get_vflip
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "analog gain",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = vv6410_set_analog_gain,
+ .get = vv6410_get_analog_gain
+ }
+ },
+
+ .nmodes = 1,
+ .modes = {
+ {
+ 356,
+ 292,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 356 * 292,
+ .bytesperline = 356,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }
+ }
+};
+
+/* If NULL, only single value to write, stored in len */
+struct stv_init {
+ const u8 *data;
+ u16 start;
+ u8 len;
+};
+
+static const u8 x1500[] = { /* 0x1500 - 0x150f */
+ 0x0b, 0xa7, 0xb7, 0x00, 0x00
+};
+
+static const u8 x1536[] = { /* 0x1536 - 0x153b */
+ 0x02, 0x00, 0x60, 0x01, 0x20, 0x01
+};
+
+static const u8 x15c1[] = { /* 0x15c1 - 0x15c2 */
+ 0xff, 0x03 /* Output word 0x03ff = 1023 (ISO size) */
+};
+
+static const struct stv_init stv_bridge_init[] = {
+ /* This reg is written twice. Some kind of reset? */
+ {NULL, 0x1620, 0x80},
+ {NULL, 0x1620, 0x00},
+ {NULL, 0x1423, 0x04},
+ {x1500, 0x1500, ARRAY_SIZE(x1500)},
+ {x1536, 0x1536, ARRAY_SIZE(x1536)},
+ {x15c1, 0x15c1, ARRAY_SIZE(x15c1)}
+};
+
+static const u8 vv6410_sensor_init[][2] = {
+ /* Setup registers */
+ {VV6410_SETUP0, VV6410_SOFT_RESET},
+ {VV6410_SETUP0, VV6410_LOW_POWER_MODE},
+ /* Use shuffled read-out mode */
+ {VV6410_SETUP1, BIT(6)},
+ /* All modes to 1 */
+ {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)},
+ {VV6410_PINMAPPING, 0x00},
+ /* Pre-clock generator divide off */
+ {VV6410_DATAFORMAT, BIT(7) | BIT(0)},
+
+ /* Exposure registers */
+ {VV6410_FINEH, VV6410_FINE_EXPOSURE >> 8},
+ {VV6410_FINEL, VV6410_FINE_EXPOSURE & 0xff},
+ {VV6410_COARSEH, VV6410_COARSE_EXPOSURE >> 8},
+ {VV6410_COARSEL, VV6410_COARSE_EXPOSURE & 0xff},
+ {VV6410_ANALOGGAIN, 0xf0 | VV6410_DEFAULT_GAIN},
+ {VV6410_CLKDIV, VV6410_CLK_DIV_2},
+
+ /* System registers */
+ /* Enable voltage doubler */
+ {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
+ {VV6410_AT0, 0x00},
+ /* Power up audio, differential */
+ {VV6410_AT1, BIT(4)|BIT(0)},
+};
+
+#endif
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
index bd9288665a80..6d904d5e4c74 100644
--- a/drivers/media/video/gspca/sunplus.c
+++ b/drivers/media/video/gspca/sunplus.c
@@ -123,7 +123,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -136,7 +136,7 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 1},
};
-static struct v4l2_pix_format custom_mode[] = {
+static const struct v4l2_pix_format custom_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -149,7 +149,7 @@ static struct v4l2_pix_format custom_mode[] = {
.priv = 1},
};
-static struct v4l2_pix_format vga_mode2[] = {
+static const struct v4l2_pix_format vga_mode2[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
index eac245d7a756..6ee111a3cbd1 100644
--- a/drivers/media/video/gspca/t613.c
+++ b/drivers/media/video/gspca/t613.c
@@ -233,7 +233,7 @@ static char *effects_control[] = {
"Negative",
};
-static struct v4l2_pix_format vga_mode_t16[] = {
+static const struct v4l2_pix_format vga_mode_t16[] = {
{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 4 / 8 + 590,
@@ -499,7 +499,7 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev)
reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset);
msleep(5);
i = 4;
- while (--i < 0) {
+ while (--i > 0) {
byte = reg_r(gspca_dev, 0x0060);
if (!(byte & 0x01))
break;
diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c
index 968a5911704f..94163cceb28a 100644
--- a/drivers/media/video/gspca/tv8532.c
+++ b/drivers/media/video/gspca/tv8532.c
@@ -30,15 +30,10 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- int buflen; /* current length of tmpbuf */
- __u8 tmpbuf[352 * 288 + 10 * 288]; /* no protection... */
- __u8 tmpbuf2[352 * 288]; /* no protection... */
+ __u16 brightness;
+ __u16 contrast;
- unsigned short brightness;
- unsigned short contrast;
-
- char packet;
- char synchro;
+ __u8 packet;
};
/* V4L2 controls supported by the driver */
@@ -78,7 +73,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144,
@@ -392,6 +387,8 @@ static void setbrightness(struct gspca_dev *gspca_dev)
/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
+
reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32);
reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00);
tv_8532ReadRegisters(gspca_dev);
@@ -443,6 +440,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
/************************************************/
tv_8532_PollReg(gspca_dev);
reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */
+
+ gspca_dev->empty_packet = 0; /* check the empty packets */
+ sd->packet = 0; /* ignore the first packets */
+
return 0;
}
@@ -451,111 +452,36 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b);
}
-static void tv8532_preprocess(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-/* we should received a whole frame with header and EOL marker
- * in gspca_dev->tmpbuf and return a GBRG pattern in gspca_dev->tmpbuf2
- * sequence 2bytes header the Alternate pixels bayer GB 4 bytes
- * Alternate pixels bayer RG 4 bytes EOL */
- int width = gspca_dev->width;
- int height = gspca_dev->height;
- unsigned char *dst = sd->tmpbuf2;
- unsigned char *data = sd->tmpbuf;
- int i;
-
- /* precompute where is the good bayer line */
- if (((data[3] + data[width + 7]) >> 1)
- + (data[4] >> 2)
- + (data[width + 6] >> 1) >= ((data[2] + data[width + 6]) >> 1)
- + (data[3] >> 2)
- + (data[width + 5] >> 1))
- data += 3;
- else
- data += 2;
- for (i = 0; i < height / 2; i++) {
- memcpy(dst, data, width);
- data += width + 3;
- dst += width;
- memcpy(dst, data, width);
- data += width + 7;
- dst += width;
- }
-}
-
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, /* target */
__u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
-
- if (data[0] != 0x80) {
- sd->packet++;
- if (sd->buflen + len > sizeof sd->tmpbuf) {
- if (gspca_dev->last_packet_type != DISCARD_PACKET) {
- PDEBUG(D_PACK, "buffer overflow");
- gspca_dev->last_packet_type = DISCARD_PACKET;
- }
- return;
- }
- memcpy(&sd->tmpbuf[sd->buflen], data, len);
- sd->buflen += len;
- return;
- }
-
- /* here we detect 0x80 */
- /* counter is limited so we need few header for a frame :) */
-
- /* header 0x80 0x80 0x80 0x80 0x80 */
- /* packet 00 63 127 145 00 */
- /* sof 0 1 1 0 0 */
-
- /* update sequence */
- if (sd->packet == 63 || sd->packet == 127)
- sd->synchro = 1;
-
- /* is there a frame start ? */
- if (sd->packet >= (gspca_dev->height >> 1) - 1) {
- PDEBUG(D_PACK, "SOF > %d packet %d", sd->synchro,
- sd->packet);
- if (!sd->synchro) { /* start of frame */
- if (gspca_dev->last_packet_type == FIRST_PACKET) {
- tv8532_preprocess(gspca_dev);
- frame = gspca_frame_add(gspca_dev,
- LAST_PACKET,
- frame, sd->tmpbuf2,
- gspca_dev->width *
- gspca_dev->width);
- }
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- frame, data, 0);
- memcpy(sd->tmpbuf, data, len);
- sd->buflen = len;
- sd->packet = 0;
- return;
- }
- if (gspca_dev->last_packet_type != DISCARD_PACKET) {
- PDEBUG(D_PACK,
- "Warning wrong TV8532 frame detection %d",
- sd->packet);
- gspca_dev->last_packet_type = DISCARD_PACKET;
- }
- return;
- }
-
- if (!sd->synchro) {
- /* Drop packet frame corrupt */
- PDEBUG(D_PACK, "DROP SOF %d packet %d",
- sd->synchro, sd->packet);
- sd->packet = 0;
- gspca_dev->last_packet_type = DISCARD_PACKET;
- return;
- }
- sd->synchro = 1;
- sd->packet++;
- memcpy(&sd->tmpbuf[sd->buflen], data, len);
- sd->buflen += len;
+ int packet_type0, packet_type1;
+
+ packet_type0 = packet_type1 = INTER_PACKET;
+ if (gspca_dev->empty_packet) {
+ gspca_dev->empty_packet = 0;
+ sd->packet = gspca_dev->height / 2;
+ packet_type0 = FIRST_PACKET;
+ } else if (sd->packet == 0)
+ return; /* 2 more lines in 352x288 ! */
+ sd->packet--;
+ if (sd->packet == 0)
+ packet_type1 = LAST_PACKET;
+
+ /* each packet contains:
+ * - header 2 bytes
+ * - RG line
+ * - 4 bytes
+ * - GB line
+ * - 4 bytes
+ */
+ gspca_frame_add(gspca_dev, packet_type0,
+ frame, data + 2, gspca_dev->width);
+ gspca_frame_add(gspca_dev, packet_type1,
+ frame, data + gspca_dev->width + 6, gspca_dev->width);
}
static void setcontrast(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
index 17af353ddd1c..0525ea51a6de 100644
--- a/drivers/media/video/gspca/vc032x.c
+++ b/drivers/media/video/gspca/vc032x.c
@@ -32,44 +32,68 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- unsigned char autogain;
- unsigned char lightfreq;
+ __u8 hflip;
+ __u8 vflip;
+ __u8 lightfreq;
+ __u8 sharpness;
- char qindex;
char bridge;
#define BRIDGE_VC0321 0
#define BRIDGE_VC0323 1
char sensor;
#define SENSOR_HV7131R 0
-#define SENSOR_MI1320 1
-#define SENSOR_MI1310_SOC 2
-#define SENSOR_OV7660 3
-#define SENSOR_OV7670 4
-#define SENSOR_PO3130NC 5
+#define SENSOR_MI0360 1
+#define SENSOR_MI1320 2
+#define SENSOR_MI1310_SOC 3
+#define SENSOR_OV7660 4
+#define SENSOR_OV7670 5
+#define SENSOR_PO1200 6
+#define SENSOR_PO3130NC 7
};
/* V4L2 controls supported by the driver */
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
+/* next 2 controls work with ov7660 and ov7670 only */
+#define HFLIP_IDX 0
{
{
- .id = V4L2_CID_AUTOGAIN,
+ .id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
+ .name = "Mirror",
.minimum = 0,
.maximum = 1,
.step = 1,
-#define AUTOGAIN_DEF 1
- .default_value = AUTOGAIN_DEF,
+#define HFLIP_DEF 0
+ .default_value = HFLIP_DEF,
},
- .set = sd_setautogain,
- .get = sd_getautogain,
+ .set = sd_sethflip,
+ .get = sd_gethflip,
},
-#define LIGHTFREQ_IDX 1
+#define VFLIP_IDX 1
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define VFLIP_DEF 0
+ .default_value = VFLIP_DEF,
+ },
+ .set = sd_setvflip,
+ .get = sd_getvflip,
+ },
+#define LIGHTFREQ_IDX 2
{
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
@@ -84,9 +108,25 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setfreq,
.get = sd_getfreq,
},
+/* po1200 only */
+#define SHARPNESS_IDX 3
+ {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 2,
+ .step = 1,
+#define SHARPNESS_DEF 1
+ .default_value = SHARPNESS_DEF,
+ },
+ .set = sd_setsharpness,
+ .get = sd_getsharpness,
+ },
};
-static struct v4l2_pix_format vc0321_mode[] = {
+static const struct v4l2_pix_format vc0321_mode[] = {
{320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 2,
@@ -98,7 +138,7 @@ static struct v4l2_pix_format vc0321_mode[] = {
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0},
};
-static struct v4l2_pix_format vc0323_mode[] = {
+static const struct v4l2_pix_format vc0323_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -111,6 +151,252 @@ static struct v4l2_pix_format vc0323_mode[] = {
.priv = 0},
};
+static const struct v4l2_pix_format svga_mode[] = {
+ {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600 * 1 / 4 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/* OV7660/7670 registers */
+#define OV7660_REG_MVFP 0x1e
+#define OV7660_MVFP_MIRROR 0x20
+#define OV7660_MVFP_VFLIP 0x10
+
+static const __u8 mi0360_matrix[9] = {
+ 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50
+};
+
+static const __u8 mi0360_initVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xb8, 0x00, 0x13, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x08, 0xe0, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x14, 0x18, 0xcc},
+ {0xb8, 0xb2, 0x0a, 0xcc},
+ {0xb8, 0xb4, 0x0a, 0xcc},
+ {0xb8, 0xb5, 0x0a, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc},
+ {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc},
+ {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc},
+ {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc},
+ {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc},
+ {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x31, 0x00, 0x00, 0xbb},
+ {0x09, 0x01, 0xc7, 0xbb},
+ {0x34, 0x01, 0x00, 0xbb},
+ {0x2b, 0x00, 0x28, 0xbb},
+ {0x2c, 0x00, 0x30, 0xbb},
+ {0x2d, 0x00, 0x30, 0xbb},
+ {0x2e, 0x00, 0x28, 0xbb},
+ {0x62, 0x04, 0x11, 0xbb},
+ {0x03, 0x01, 0xe0, 0xbb},
+ {0x2c, 0x00, 0x2c, 0xbb},
+ {0x20, 0xd0, 0x00, 0xbb},
+ {0x01, 0x00, 0x08, 0xbb},
+ {0x06, 0x00, 0x10, 0xbb},
+ {0x05, 0x00, 0x20, 0xbb},
+ {0x20, 0x00, 0x00, 0xbb},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x02, 0xcc},
+ {0xb6, 0x02, 0x80, 0xcc},
+ {0xb6, 0x05, 0x01, 0xcc},
+ {0xb6, 0x04, 0xe0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x02, 0xcc},
+ {0xb6, 0x17, 0x58, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0x35, 0x00, 0x60, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+static const __u8 mi0360_initQVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xbc, 0x00, 0xd1, 0xcc},
+ {0xb8, 0x00, 0x13, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x08, 0xe0, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x14, 0x18, 0xcc},
+ {0xb8, 0xb2, 0x0a, 0xcc},
+ {0xb8, 0xb4, 0x0a, 0xcc},
+ {0xb8, 0xb5, 0x0a, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc},
+ {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc},
+ {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc},
+ {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc},
+ {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc},
+ {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x31, 0x00, 0x00, 0xbb},
+ {0x09, 0x01, 0xc7, 0xbb},
+ {0x34, 0x01, 0x00, 0xbb},
+ {0x2b, 0x00, 0x28, 0xbb},
+ {0x2c, 0x00, 0x30, 0xbb},
+ {0x2d, 0x00, 0x30, 0xbb},
+ {0x2e, 0x00, 0x28, 0xbb},
+ {0x62, 0x04, 0x11, 0xbb},
+ {0x03, 0x01, 0xe0, 0xbb},
+ {0x2c, 0x00, 0x2c, 0xbb},
+ {0x20, 0xd0, 0x00, 0xbb},
+ {0x01, 0x00, 0x08, 0xbb},
+ {0x06, 0x00, 0x10, 0xbb},
+ {0x05, 0x00, 0x20, 0xbb},
+ {0x20, 0x00, 0x00, 0xbb},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x01, 0xcc},
+ {0xb6, 0x02, 0x40, 0xcc},
+ {0xb6, 0x05, 0x00, 0xcc},
+ {0xb6, 0x04, 0xf0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x00, 0xcc},
+ {0xb6, 0x17, 0x96, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0x35, 0x00, 0xef, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+
static const __u8 mi1310_socinitVGA_JPG[][4] = {
{0xb0, 0x03, 0x19, 0xcc},
{0xb0, 0x04, 0x02, 0xcc},
@@ -823,7 +1109,7 @@ static const __u8 ov7660_initVGA_data[][4] = {
{0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa},
{0x00, 0x12, 0x80, 0xaa},
{0x00, 0x12, 0x05, 0xaa},
- {0x00, 0x1e, 0x01, 0xaa},
+ {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */
{0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
{0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
{0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa},
@@ -877,7 +1163,7 @@ static const __u8 ov7660_initQVGA_data[][4] = {
{0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc},
{0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa},
{0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa},
- {0x00, 0x1e, 0x01, 0xaa},
+ {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */
{0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
{0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
{0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa},
@@ -983,7 +1269,8 @@ static const __u8 ov7670_initVGA_JPG[][4] = {
{0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa},
{0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa},
{0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa},
- {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa},
+ {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */
+ {0x00, 0x21, 0x02, 0xaa},
{0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa},
{0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa},
{0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa},
@@ -1048,7 +1335,8 @@ static const __u8 ov7670_initVGA_JPG[][4] = {
{0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa},
{0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa},
{0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa},
- {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */
+ {0x00, 0xaa, 0x14, 0xaa},
{0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa},
{0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa},
{0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa},
@@ -1110,7 +1398,8 @@ static const __u8 ov7670_initQVGA_JPG[][4] = {
{0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa},
{0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa},
{0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa},
- {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa},
+ {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */
+ {0x00, 0x21, 0x02, 0xaa},
{0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa},
{0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa},
{0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa},
@@ -1175,7 +1464,8 @@ static const __u8 ov7670_initQVGA_JPG[][4] = {
{0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa},
{0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa},
{0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa},
- {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */
+ {0x00, 0xaa, 0x14, 0xaa},
{0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa},
{0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa},
{0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa},
@@ -1204,6 +1494,275 @@ static const __u8 ov7670_initQVGA_JPG[][4] = {
{},
};
+/* PO1200 - values from usbvm326.inf and ms-win trace */
+static const __u8 po1200_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const __u8 po1200_matrix[9] = {
+ 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e
+};
+static const __u8 po1200_initVGA_data[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc}, /* reset? */
+ {0xb0, 0x03, 0x19, 0xcc},
+/* {0x00, 0x00, 0x33, 0xdd}, */
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb0, 0x02, 0x02, 0xcc},
+ {0xb3, 0x5d, 0x00, 0xcc},
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x02, 0xb2, 0xcc},
+ {0xb3, 0x03, 0x18, 0xcc},
+ {0xb3, 0x04, 0x15, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x02, 0xcc},
+ {0xb3, 0x23, 0x58, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x03, 0xcc},
+ {0xb3, 0x17, 0x1f, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb0, 0x54, 0x13, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xdc, 0xcc},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x12, 0x05, 0xaa},
+ {0x00, 0x13, 0x02, 0xaa},
+ {0x00, 0x1e, 0xc6, 0xaa}, /* h/v flip */
+ {0x00, 0x21, 0x00, 0xaa},
+ {0x00, 0x25, 0x02, 0xaa},
+ {0x00, 0x3c, 0x4f, 0xaa},
+ {0x00, 0x3f, 0xe0, 0xaa},
+ {0x00, 0x42, 0xff, 0xaa},
+ {0x00, 0x45, 0x34, 0xaa},
+ {0x00, 0x55, 0xfe, 0xaa},
+ {0x00, 0x59, 0xd3, 0xaa},
+ {0x00, 0x5e, 0x04, 0xaa},
+ {0x00, 0x61, 0xb8, 0xaa}, /* sharpness */
+ {0x00, 0x62, 0x02, 0xaa},
+ {0x00, 0xa7, 0x31, 0xaa},
+ {0x00, 0xa9, 0x66, 0xaa},
+ {0x00, 0xb0, 0x00, 0xaa},
+ {0x00, 0xb1, 0x00, 0xaa},
+ {0x00, 0xb3, 0x11, 0xaa},
+ {0x00, 0xb6, 0x26, 0xaa},
+ {0x00, 0xb7, 0x20, 0xaa},
+ {0x00, 0xba, 0x04, 0xaa},
+ {0x00, 0x88, 0x42, 0xaa},
+ {0x00, 0x89, 0x9a, 0xaa},
+ {0x00, 0x8a, 0x88, 0xaa},
+ {0x00, 0x8b, 0x8e, 0xaa},
+ {0x00, 0x8c, 0x3e, 0xaa},
+ {0x00, 0x8d, 0x90, 0xaa},
+ {0x00, 0x8e, 0x87, 0xaa},
+ {0x00, 0x8f, 0x96, 0xaa},
+ {0x00, 0x90, 0x3d, 0xaa},
+ {0x00, 0x64, 0x00, 0xaa},
+ {0x00, 0x65, 0x10, 0xaa},
+ {0x00, 0x66, 0x20, 0xaa},
+ {0x00, 0x67, 0x2b, 0xaa},
+ {0x00, 0x68, 0x36, 0xaa},
+ {0x00, 0x69, 0x49, 0xaa},
+ {0x00, 0x6a, 0x5a, 0xaa},
+ {0x00, 0x6b, 0x7f, 0xaa},
+ {0x00, 0x6c, 0x9b, 0xaa},
+ {0x00, 0x6d, 0xba, 0xaa},
+ {0x00, 0x6e, 0xd4, 0xaa},
+ {0x00, 0x6f, 0xea, 0xaa},
+ {0x00, 0x70, 0x00, 0xaa},
+ {0x00, 0x71, 0x10, 0xaa},
+ {0x00, 0x72, 0x20, 0xaa},
+ {0x00, 0x73, 0x2b, 0xaa},
+ {0x00, 0x74, 0x36, 0xaa},
+ {0x00, 0x75, 0x49, 0xaa},
+ {0x00, 0x76, 0x5a, 0xaa},
+ {0x00, 0x77, 0x7f, 0xaa},
+ {0x00, 0x78, 0x9b, 0xaa},
+ {0x00, 0x79, 0xba, 0xaa},
+ {0x00, 0x7a, 0xd4, 0xaa},
+ {0x00, 0x7b, 0xea, 0xaa},
+ {0x00, 0x7c, 0x00, 0xaa},
+ {0x00, 0x7d, 0x10, 0xaa},
+ {0x00, 0x7e, 0x20, 0xaa},
+ {0x00, 0x7f, 0x2b, 0xaa},
+ {0x00, 0x80, 0x36, 0xaa},
+ {0x00, 0x81, 0x49, 0xaa},
+ {0x00, 0x82, 0x5a, 0xaa},
+ {0x00, 0x83, 0x7f, 0xaa},
+ {0x00, 0x84, 0x9b, 0xaa},
+ {0x00, 0x85, 0xba, 0xaa},
+ {0x00, 0x86, 0xd4, 0xaa},
+ {0x00, 0x87, 0xea, 0xaa},
+ {0x00, 0x57, 0x2a, 0xaa},
+ {0x00, 0x03, 0x01, 0xaa},
+ {0x00, 0x04, 0x10, 0xaa},
+ {0x00, 0x05, 0x10, 0xaa},
+ {0x00, 0x06, 0x10, 0xaa},
+ {0x00, 0x07, 0x10, 0xaa},
+ {0x00, 0x08, 0x13, 0xaa},
+ {0x00, 0x0a, 0x00, 0xaa},
+ {0x00, 0x0b, 0x10, 0xaa},
+ {0x00, 0x0c, 0x20, 0xaa},
+ {0x00, 0x0d, 0x18, 0xaa},
+ {0x00, 0x22, 0x01, 0xaa},
+ {0x00, 0x23, 0x60, 0xaa},
+ {0x00, 0x25, 0x08, 0xaa},
+ {0x00, 0x26, 0x82, 0xaa},
+ {0x00, 0x2e, 0x0f, 0xaa},
+ {0x00, 0x2f, 0x1e, 0xaa},
+ {0x00, 0x30, 0x2d, 0xaa},
+ {0x00, 0x31, 0x3c, 0xaa},
+ {0x00, 0x32, 0x4b, 0xaa},
+ {0x00, 0x33, 0x5a, 0xaa},
+ {0x00, 0x34, 0x69, 0xaa},
+ {0x00, 0x35, 0x78, 0xaa},
+ {0x00, 0x36, 0x87, 0xaa},
+ {0x00, 0x37, 0x96, 0xaa},
+ {0x00, 0x38, 0xa5, 0xaa},
+ {0x00, 0x39, 0xb4, 0xaa},
+ {0x00, 0x3a, 0xc3, 0xaa},
+ {0x00, 0x3b, 0xd2, 0xaa},
+ {0x00, 0x3c, 0xe1, 0xaa},
+ {0x00, 0x3e, 0xff, 0xaa},
+ {0x00, 0x3f, 0xff, 0xaa},
+ {0x00, 0x40, 0xff, 0xaa},
+ {0x00, 0x41, 0xff, 0xaa},
+ {0x00, 0x42, 0xff, 0xaa},
+ {0x00, 0x43, 0xff, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x20, 0xc4, 0xaa},
+ {0x00, 0x13, 0x03, 0xaa},
+ {0x00, 0x3c, 0x50, 0xaa},
+ {0x00, 0x61, 0x6a, 0xaa}, /* sharpness? */
+ {0x00, 0x51, 0x5b, 0xaa},
+ {0x00, 0x52, 0x91, 0xaa},
+ {0x00, 0x53, 0x4c, 0xaa},
+ {0x00, 0x54, 0x50, 0xaa},
+ {0x00, 0x56, 0x02, 0xaa},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x03, 0xcc},
+ {0xb6, 0x02, 0x20, 0xcc},
+ {0xb6, 0x05, 0x02, 0xcc},
+ {0xb6, 0x04, 0x58, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x21, 0xcc},
+ {0xb6, 0x18, 0x03, 0xcc},
+ {0xb6, 0x17, 0xa9, 0xcc},
+ {0xb6, 0x16, 0x80, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x00, 0xcc},
+ {0xb8, 0x06, 0x20, 0xcc},
+ {0xb8, 0x07, 0x03, 0xcc},
+ {0xb8, 0x08, 0x58, 0xcc},
+ {0xb8, 0x09, 0x02, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xd9, 0x0f, 0xaa},
+ {0x00, 0xda, 0xaa, 0xaa},
+ {0x00, 0xd9, 0x10, 0xaa},
+ {0x00, 0xda, 0xaa, 0xaa},
+ {0x00, 0xd9, 0x11, 0xaa},
+ {0x00, 0xda, 0x00, 0xaa},
+ {0x00, 0xd9, 0x12, 0xaa},
+ {0x00, 0xda, 0xff, 0xaa},
+ {0x00, 0xd9, 0x13, 0xaa},
+ {0x00, 0xda, 0xff, 0xaa},
+ {0x00, 0xe8, 0x11, 0xaa},
+ {0x00, 0xe9, 0x12, 0xaa},
+ {0x00, 0xea, 0x5c, 0xaa},
+ {0x00, 0xeb, 0xff, 0xaa},
+ {0x00, 0xd8, 0x80, 0xaa},
+ {0x00, 0xe6, 0x02, 0xaa},
+ {0x00, 0xd6, 0x40, 0xaa},
+ {0x00, 0xe3, 0x05, 0xaa},
+ {0x00, 0xe0, 0x40, 0xaa},
+ {0x00, 0xde, 0x03, 0xaa},
+ {0x00, 0xdf, 0x03, 0xaa},
+ {0x00, 0xdb, 0x02, 0xaa},
+ {0x00, 0xdc, 0x00, 0xaa},
+ {0x00, 0xdd, 0x03, 0xaa},
+ {0x00, 0xe1, 0x08, 0xaa},
+ {0x00, 0xe2, 0x01, 0xaa},
+ {0x00, 0xd6, 0x40, 0xaa},
+ {0x00, 0xe4, 0x40, 0xaa},
+ {0x00, 0xa8, 0x8f, 0xaa},
+ {0x00, 0xb4, 0x16, 0xaa},
+ {0xb0, 0x02, 0x06, 0xcc},
+ {0xb0, 0x18, 0x06, 0xcc},
+ {0xb0, 0x19, 0x06, 0xcc},
+ {0xb3, 0x5d, 0x18, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0x00, 0xb4, 0x0e, 0xaa},
+ {0x00, 0xb5, 0x49, 0xaa},
+ {0x00, 0xb6, 0x1c, 0xaa},
+ {0x00, 0xb7, 0x96, 0xaa},
+/* end of usbvm326.inf - start of ms-win trace */
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x3d, 0xcc},
+/*read b306*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x1a, 0x09, 0xaa},
+ {0x00, 0x1b, 0x8a, 0xaa},
+/*read b827*/
+ {0xb8, 0x27, 0x00, 0xcc},
+ {0xb8, 0x26, 0x60, 0xcc},
+ {0xb8, 0x26, 0x60, 0xcc},
+/*gamma - to do?*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xae, 0x84, 0xaa},
+/*gamma again*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x96, 0xa0, 0xaa},
+/*matrix*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x91, 0x35, 0xaa},
+ {0x00, 0x92, 0x22, 0xaa},
+/*gamma*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x95, 0x85, 0xaa},
+/*matrix*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x4d, 0x20, 0xaa},
+ {0xb8, 0x22, 0x40, 0xcc},
+ {0xb8, 0x23, 0x40, 0xcc},
+ {0xb8, 0x24, 0x40, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x00, 0x00, 0x64, 0xdd},
+ {0x00, 0x03, 0x01, 0xaa},
+/*read 46*/
+ {0x00, 0x46, 0x3c, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x16, 0x40, 0xaa},
+ {0x00, 0x17, 0x40, 0xaa},
+ {0x00, 0x18, 0x40, 0xaa},
+ {0x00, 0x19, 0x41, 0xaa},
+ {0x00, 0x03, 0x01, 0xaa},
+ {0x00, 0x46, 0x3c, 0xaa},
+ {0x00, 0x00, 0x18, 0xdd},
+/*read bfff*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xb4, 0x1c, 0xaa},
+ {0x00, 0xb5, 0x92, 0xaa},
+ {0x00, 0xb6, 0x39, 0xaa},
+ {0x00, 0xb7, 0x24, 0xaa},
+/*write 89 0400 1415*/
+};
+
struct sensor_info {
int sensorId;
__u8 I2cAdd;
@@ -1222,6 +1781,9 @@ static const struct sensor_info sensor_info_data[] = {
{SENSOR_MI1320, 0x80 | 0xc8, 0x00, 0x148c, 0x64, 0x65, 0x01},
{SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05},
{SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
+/* (tested in vc032x_probe_sensor) */
+/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
+ {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
};
/* read 'len' bytes in gspca_dev->usb_buf */
@@ -1278,18 +1840,18 @@ static void read_sensor_register(struct gspca_dev *gspca_dev,
msleep(1);
}
reg_r(gspca_dev, 0xa1, 0xb33e, 1);
- hdata = gspca_dev->usb_buf[0];
+ ldata = gspca_dev->usb_buf[0];
reg_r(gspca_dev, 0xa1, 0xb33d, 1);
mdata = gspca_dev->usb_buf[0];
reg_r(gspca_dev, 0xa1, 0xb33c, 1);
- ldata = gspca_dev->usb_buf[0];
- PDEBUG(D_PROBE, "Read Sensor h (0x%02X) m (0x%02X) l (0x%02X)",
+ hdata = gspca_dev->usb_buf[0];
+ PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x",
hdata, mdata, ldata);
reg_r(gspca_dev, 0xa1, 0xb334, 1);
if (gspca_dev->usb_buf[0] == 0x02)
- *value = (ldata << 8) + mdata;
+ *value = (hdata << 8) + mdata;
else
- *value = ldata;
+ *value = hdata;
}
static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
@@ -1300,7 +1862,7 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
const struct sensor_info *ptsensor_info;
reg_r(gspca_dev, 0xa1, 0xbfcf, 1);
- PDEBUG(D_PROBE, "check sensor header %d", gspca_dev->usb_buf[0]);
+ PDEBUG(D_PROBE, "check sensor header %02x", gspca_dev->usb_buf[0]);
for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) {
ptsensor_info = &sensor_info_data[i];
reg_w(dev, 0xa0, 0x02, 0xb334);
@@ -1309,16 +1871,15 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
reg_w(dev, 0xa0, 0x01, 0xb308);
reg_w(dev, 0xa0, 0x0c, 0xb309);
reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335);
-/* PDEBUG(D_PROBE,
- "check sensor VC032X -> %d Add -> ox%02X!",
- i, ptsensor_info->I2cAdd); */
reg_w(dev, 0xa0, ptsensor_info->op, 0xb301);
read_sensor_register(gspca_dev, ptsensor_info->IdAdd, &value);
- if (value == ptsensor_info->VpId) {
-/* PDEBUG(D_PROBE, "find sensor VC032X -> ox%04X!",
- ptsensor_info->VpId); */
+ if (value == ptsensor_info->VpId)
return ptsensor_info->sensorId;
- }
+
+ /* special case for MI0360 */
+ if (ptsensor_info->sensorId == SENSOR_MI1310_SOC
+ && value == 0x8243)
+ return SENSOR_MI0360;
}
return -1;
}
@@ -1420,13 +1981,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
cam->epaddr = 0x02;
sd->bridge = id->driver_info;
- if (sd->bridge == BRIDGE_VC0321) {
- cam->cam_mode = vc0321_mode;
- cam->nmodes = ARRAY_SIZE(vc0321_mode);
- } else {
- cam->cam_mode = vc0323_mode;
- cam->nmodes = ARRAY_SIZE(vc0323_mode);
- }
vc0321_reset(gspca_dev);
sensor = vc032x_probe_sensor(gspca_dev);
@@ -1436,35 +1990,66 @@ static int sd_config(struct gspca_dev *gspca_dev,
return -EINVAL;
case SENSOR_HV7131R:
PDEBUG(D_PROBE, "Find Sensor HV7131R");
- sd->sensor = SENSOR_HV7131R;
+ break;
+ case SENSOR_MI0360:
+ PDEBUG(D_PROBE, "Find Sensor MI0360");
+ sd->bridge = BRIDGE_VC0323;
break;
case SENSOR_MI1310_SOC:
PDEBUG(D_PROBE, "Find Sensor MI1310_SOC");
- sd->sensor = SENSOR_MI1310_SOC;
break;
case SENSOR_MI1320:
PDEBUG(D_PROBE, "Find Sensor MI1320");
- sd->sensor = SENSOR_MI1320;
break;
case SENSOR_OV7660:
PDEBUG(D_PROBE, "Find Sensor OV7660");
- sd->sensor = SENSOR_OV7660;
break;
case SENSOR_OV7670:
PDEBUG(D_PROBE, "Find Sensor OV7670");
- sd->sensor = SENSOR_OV7670;
+ break;
+ case SENSOR_PO1200:
+ PDEBUG(D_PROBE, "Find Sensor PO1200");
break;
case SENSOR_PO3130NC:
PDEBUG(D_PROBE, "Find Sensor PO3130NC");
- sd->sensor = SENSOR_PO3130NC;
break;
}
+ sd->sensor = sensor;
- sd->qindex = 7;
- sd->autogain = AUTOGAIN_DEF;
+ if (sd->bridge == BRIDGE_VC0321) {
+ cam->cam_mode = vc0321_mode;
+ cam->nmodes = ARRAY_SIZE(vc0321_mode);
+ } else {
+ if (sensor != SENSOR_PO1200) {
+ cam->cam_mode = vc0323_mode;
+ cam->nmodes = ARRAY_SIZE(vc0323_mode);
+ } else {
+ cam->cam_mode = svga_mode;
+ cam->nmodes = ARRAY_SIZE(svga_mode);
+ }
+ }
+
+ sd->hflip = HFLIP_DEF;
+ sd->vflip = VFLIP_DEF;
+ if (sd->sensor == SENSOR_OV7670) {
+ sd->hflip = 1;
+ sd->vflip = 1;
+ }
sd->lightfreq = FREQ_DEF;
if (sd->sensor != SENSOR_OV7670)
gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX);
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ case SENSOR_OV7670:
+ case SENSOR_PO1200:
+ break;
+ default:
+ gspca_dev->ctrl_dis = (1 << HFLIP_IDX)
+ | (1 << VFLIP_IDX);
+ break;
+ }
+
+ sd->sharpness = SHARPNESS_DEF;
if (sd->bridge == BRIDGE_VC0321) {
reg_r(gspca_dev, 0x8a, 0, 3);
@@ -1482,12 +2067,33 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void setquality(struct gspca_dev *gspca_dev)
+/* for OV7660 and OV7670 only */
+static void sethvflip(struct gspca_dev *gspca_dev)
{
-}
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 data;
-static void setautogain(struct gspca_dev *gspca_dev)
-{
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ data = 1;
+ break;
+ case SENSOR_OV7670:
+ data = 7;
+ break;
+ case SENSOR_PO1200:
+ data = 0;
+ i2c_write(gspca_dev, 0x03, &data, 1);
+ data = 0x80 * sd->hflip
+ | 0x40 * sd->vflip
+ | 0x06;
+ i2c_write(gspca_dev, 0x1e, &data, 1);
+ return;
+ default:
+ return;
+ }
+ data |= OV7660_MVFP_MIRROR * sd->hflip
+ | OV7660_MVFP_VFLIP * sd->vflip;
+ i2c_write(gspca_dev, OV7660_REG_MVFP, &data, 1);
}
static void setlightfreq(struct gspca_dev *gspca_dev)
@@ -1501,6 +2107,20 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]);
}
+/* po1200 only */
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 data;
+
+ if (sd->sensor != SENSOR_PO1200)
+ return;
+ data = 0;
+ i2c_write(gspca_dev, 0x03, &data, 1);
+ data = 0xb5 + sd->sharpness * 3;
+ i2c_write(gspca_dev, 0x61, &data, 1);
+}
+
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1551,6 +2171,17 @@ static int sd_start(struct gspca_dev *gspca_dev)
usb_exchange(gspca_dev, ov7670_initVGA_JPG);
}
break;
+ case SENSOR_MI0360:
+ GammaT = mi1320_gamma;
+ MatrixT = mi0360_matrix;
+ if (mode) {
+ /* 320x240 */
+ usb_exchange(gspca_dev, mi0360_initQVGA_JPG);
+ } else {
+ /* 640x480 */
+ usb_exchange(gspca_dev, mi0360_initVGA_JPG);
+ }
+ break;
case SENSOR_MI1310_SOC:
if (mode) {
/* 320x240 */
@@ -1583,6 +2214,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
}
usb_exchange(gspca_dev, po3130_rundata);
break;
+ case SENSOR_PO1200:
+ GammaT = po1200_gamma;
+ MatrixT = po1200_matrix;
+ usb_exchange(gspca_dev, po1200_initVGA_data);
+ break;
default:
PDEBUG(D_PROBE, "Damned !! no sensor found Bye");
return -EMEDIUMTYPE;
@@ -1615,11 +2251,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev->dev, 0xa0, 0x23, 0xb800); * ISP CTRL_BAS
*/
/* set the led on 0x0892 0x0896 */
- reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff);
- msleep(100);
- setquality(gspca_dev);
- setautogain(gspca_dev);
- setlightfreq(gspca_dev);
+ if (sd->sensor != SENSOR_PO1200) {
+ reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff);
+ msleep(100);
+ sethvflip(gspca_dev);
+ setlightfreq(gspca_dev);
+ } else {
+ setsharpness(gspca_dev);
+ sethvflip(gspca_dev);
+ reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415);
+ }
}
return 0;
}
@@ -1665,24 +2306,48 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
data, len);
return;
}
+
+ /* The vc0321 sends some additional data after sending the complete
+ * frame, we ignore this. */
+ if (sd->bridge == BRIDGE_VC0321
+ && len > frame->v4l2_buf.length - (frame->data_end - frame->data))
+ len = frame->v4l2_buf.length - (frame->data_end - frame->data);
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hflip = val;
+ if (gspca_dev->streaming)
+ sethvflip(gspca_dev);
+ return 0;
+}
+
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->hflip;
+ return 0;
+}
+
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- sd->autogain = val;
+ sd->vflip = val;
if (gspca_dev->streaming)
- setautogain(gspca_dev);
+ sethvflip(gspca_dev);
return 0;
}
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->autogain;
+ *val = sd->vflip;
return 0;
}
@@ -1704,6 +2369,24 @@ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->sharpness = val;
+ if (gspca_dev->streaming)
+ setsharpness(gspca_dev);
+ return 0;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->sharpness;
+ return 0;
+}
+
static int sd_querymenu(struct gspca_dev *gspca_dev,
struct v4l2_querymenu *menu)
{
@@ -1743,11 +2426,13 @@ static const struct sd_desc sd_desc = {
static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x046d, 0x0892), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x046d, 0x0896), .driver_info = BRIDGE_VC0321},
+ {USB_DEVICE(0x046d, 0x0897), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x0ac8, 0x0321), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x0ac8, 0x0323), .driver_info = BRIDGE_VC0323},
{USB_DEVICE(0x0ac8, 0x0328), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x0ac8, 0xc001), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x0ac8, 0xc002), .driver_info = BRIDGE_VC0321},
+ {USB_DEVICE(0x15b8, 0x6002), .driver_info = BRIDGE_VC0323},
{USB_DEVICE(0x17ef, 0x4802), .driver_info = BRIDGE_VC0323},
{}
};
diff --git a/drivers/media/video/gspca/zc3xx-reg.h b/drivers/media/video/gspca/zc3xx-reg.h
index f52e09c2cc19..bfb559c3b713 100644
--- a/drivers/media/video/gspca/zc3xx-reg.h
+++ b/drivers/media/video/gspca/zc3xx-reg.h
@@ -244,14 +244,6 @@
#define ZC3XX_R1CA_SHARPNESS04 0x01ca
#define ZC3XX_R1CB_SHARPNESS05 0x01cb
-/* Synchronization */
-#define ZC3XX_R190_SYNC00LOW 0x0190
-#define ZC3XX_R191_SYNC00MID 0x0191
-#define ZC3XX_R192_SYNC00HIGH 0x0192
-#define ZC3XX_R195_SYNC01LOW 0x0195
-#define ZC3XX_R196_SYNC01MID 0x0196
-#define ZC3XX_R197_SYNC01HIGH 0x0197
-
/* Dead pixels */
#define ZC3XX_R250_DEADPIXELSMODE 0x0250
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index 0befacf49855..ec2a53d53fe2 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -51,16 +51,16 @@ struct sd {
#define SENSOR_CS2102 0
#define SENSOR_CS2102K 1
#define SENSOR_GC0305 2
-#define SENSOR_HDCS2020 3
-#define SENSOR_HDCS2020b 4
-#define SENSOR_HV7131B 5
-#define SENSOR_HV7131C 6
-#define SENSOR_ICM105A 7
-#define SENSOR_MC501CB 8
-#define SENSOR_OV7620 9
-/*#define SENSOR_OV7648 9 - same values */
-#define SENSOR_OV7630C 10
-#define SENSOR_PAS106 11
+#define SENSOR_HDCS2020b 3
+#define SENSOR_HV7131B 4
+#define SENSOR_HV7131C 5
+#define SENSOR_ICM105A 6
+#define SENSOR_MC501CB 7
+#define SENSOR_OV7620 8
+/*#define SENSOR_OV7648 8 - same values */
+#define SENSOR_OV7630C 9
+#define SENSOR_PAS106 10
+#define SENSOR_PAS202B 11
#define SENSOR_PB0330 12
#define SENSOR_PO2030 13
#define SENSOR_TAS5130CK 14
@@ -173,7 +173,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -186,7 +186,7 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 0},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
@@ -1653,295 +1653,6 @@ static const struct usb_action gc0305_NoFliker[] = {
{}
};
-/* play poker with registers at your own risk !! */
-static const struct usb_action hdcs2020xx_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW},
- /* D0 ?? E0 did not start */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
- {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
- {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
- {0xaa, 0x02, 0x0002},
- {0xaa, 0x07, 0x0006},
- {0xaa, 0x08, 0x0002},
- {0xaa, 0x09, 0x0006},
- {0xaa, 0x0a, 0x0001},
- {0xaa, 0x0b, 0x0001},
- {0xaa, 0x0c, 0x0008},
- {0xaa, 0x0d, 0x0000},
- {0xaa, 0x10, 0x0000},
- {0xaa, 0x12, 0x0005},
- {0xaa, 0x13, 0x0063},
- {0xaa, 0x15, 0x0070},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x70, ZC3XX_R18D_YTARGET},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x11, ZC3XX_R120_GAMMA00}, /* gamma ~4 */
- {0xa0, 0x37, ZC3XX_R121_GAMMA01},
- {0xa0, 0x58, ZC3XX_R122_GAMMA02},
- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
- {0xa0, 0x91, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa6, ZC3XX_R125_GAMMA05},
- {0xa0, 0xb8, ZC3XX_R126_GAMMA06},
- {0xa0, 0xc7, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd3, ZC3XX_R128_GAMMA08},
- {0xa0, 0xde, ZC3XX_R129_GAMMA09},
- {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xed, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
- {0xa0, 0x23, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
-
- {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf5, ZC3XX_R10B_RGB01},
- {0xa0, 0xff, ZC3XX_R10C_RGB02},
- {0xa0, 0xf9, ZC3XX_R10D_RGB10},
- {0xa0, 0x51, ZC3XX_R10E_RGB11},
- {0xa0, 0xf5, ZC3XX_R10F_RGB12},
- {0xa0, 0xfb, ZC3XX_R110_RGB20},
- {0xa0, 0xed, ZC3XX_R111_RGB21},
- {0xa0, 0x5f, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
- {0xaa, 0x20, 0x0004},
- {0xaa, 0x21, 0x003d},
- {0xaa, 0x03, 0x0041},
- {0xaa, 0x04, 0x0010},
- {0xaa, 0x05, 0x003d},
- {0xaa, 0x0e, 0x0001},
- {0xaa, 0x0f, 0x0000},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x41, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xad, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0195},
- {0xa1, 0x01, 0x0196},
- {0xa1, 0x01, 0x0197},
- {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x1d, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x85, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0116},
- {0xa1, 0x01, 0x0118},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x1d, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x85, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0116},
- {0xa1, 0x01, 0x0118},
-/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
- {0xa0, 0x00, 0x0007},
- {}
-};
-
-static const struct usb_action hdcs2020xx_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
- {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
- {0xaa, 0x02, 0x0002},
- {0xaa, 0x07, 0x0006},
- {0xaa, 0x08, 0x0002},
- {0xaa, 0x09, 0x0006},
- {0xaa, 0x0a, 0x0001},
- {0xaa, 0x0b, 0x0001},
- {0xaa, 0x0c, 0x0008},
- {0xaa, 0x0d, 0x0000},
- {0xaa, 0x10, 0x0000},
- {0xaa, 0x12, 0x0005},
- {0xaa, 0x13, 0x0063},
- {0xaa, 0x15, 0x0070},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x70, ZC3XX_R18D_YTARGET},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x11, ZC3XX_R120_GAMMA00}, /* gamma ~4*/
- {0xa0, 0x37, ZC3XX_R121_GAMMA01},
- {0xa0, 0x58, ZC3XX_R122_GAMMA02},
- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
- {0xa0, 0x91, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa6, ZC3XX_R125_GAMMA05},
- {0xa0, 0xb8, ZC3XX_R126_GAMMA06},
- {0xa0, 0xc7, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd3, ZC3XX_R128_GAMMA08},
- {0xa0, 0xde, ZC3XX_R129_GAMMA09},
- {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xed, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
- {0xa0, 0x23, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xff, ZC3XX_R10B_RGB01},
- {0xa0, 0xff, ZC3XX_R10C_RGB02},
- {0xa0, 0xff, ZC3XX_R10D_RGB10},
- {0xa0, 0x60, ZC3XX_R10E_RGB11},
- {0xa0, 0xff, ZC3XX_R10F_RGB12},
- {0xa0, 0xff, ZC3XX_R110_RGB20},
- {0xa0, 0xff, ZC3XX_R111_RGB21},
- {0xa0, 0x60, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
- {0xaa, 0x20, 0x0002},
- {0xaa, 0x21, 0x001b},
- {0xaa, 0x03, 0x0044},
- {0xaa, 0x04, 0x0008},
- {0xaa, 0x05, 0x001b},
- {0xaa, 0x0e, 0x0001},
- {0xaa, 0x0f, 0x0000},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x44, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xad, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xeb, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0195},
- {0xa1, 0x01, 0x0196},
- {0xa1, 0x01, 0x0197},
- {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x1d, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x99, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0116},
- {0xa1, 0x01, 0x0118},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x1d, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x99, ZC3XX_R118_BGAIN},
-/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
- {0xa0, 0x00, 0x0007},
-/* {0xa0, 0x18, 0x00fe}, */
- {}
-};
static const struct usb_action hdcs2020xb_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
@@ -2310,67 +2021,6 @@ static const struct usb_action hv7131bxx_Initial[] = { /* 320x240 */
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xaa, 0x02, 0x0090}, /* 00,02,80,aa */
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
- {0xa1, 0x01, 0x0091},
- {0xa1, 0x01, 0x0095},
- {0xa1, 0x01, 0x0096},
-
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x25, 0x0007},
- {0xaa, 0x26, 0x00a1},
- {0xaa, 0x27, 0x0020},
- {0xaa, 0x20, 0x0000},
- {0xaa, 0x21, 0x00a0},
- {0xaa, 0x22, 0x0016},
- {0xaa, 0x23, 0x0040},
-
- {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2F */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 4d */
- {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x07, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x16, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x40, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa1, 0x01, 0x001d},
- {0xa1, 0x01, 0x001e},
- {0xa1, 0x01, 0x001f},
- {0xa1, 0x01, 0x0020},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
-/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
{}
};
@@ -2418,65 +2068,156 @@ static const struct usb_action hv7131bxx_InitialScale[] = { /* 640x480*/
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xaa, 0x02, 0x0090}, /* {0xaa, 0x02, 0x0080}, */
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
- {0xa1, 0x01, 0x0091},
- {0xa1, 0x01, 0x0095},
- {0xa1, 0x01, 0x0096},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x25, 0x0007},
- {0xaa, 0x26, 0x00a1},
- {0xaa, 0x27, 0x0020},
- {0xaa, 0x20, 0x0000},
- {0xaa, 0x21, 0x0040},
- {0xaa, 0x22, 0x0013},
- {0xaa, 0x23, 0x004c},
- {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2f */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 4d */
- {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 60 */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x13, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x4c, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa1, 0x01, 0x001d},
- {0xa1, 0x01, 0x001e},
- {0xa1, 0x01, 0x001f},
- {0xa1, 0x01, 0x0020},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
-/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
+ {}
+};
+static const struct usb_action hv7131b_50HZ[] = { /* 640x480*/
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */
+ {0xaa, 0x22, 0x001b}, /* 00,22,1b,aa */
+ {0xaa, 0x23, 0x00fc}, /* 00,23,fc,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,ea,cc */
+ {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,60,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
+ {0xa0, 0x1b, ZC3XX_R01F_HSYNC_2}, /* 00,1f,1b,cc */
+ {0xa0, 0xfc, ZC3XX_R020_HSYNC_3}, /* 00,20,fc,cc */
+ {}
+};
+static const struct usb_action hv7131b_50HZScale[] = { /* 320x240 */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */
+ {0xaa, 0x22, 0x0012}, /* 00,22,12,aa */
+ {0xaa, 0x23, 0x0080}, /* 00,23,80,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */
+ {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,d4,cc */
+ {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,c0,cc */
+ {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */
+ {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
+ {0xa0, 0x12, ZC3XX_R01F_HSYNC_2}, /* 00,1f,12,cc */
+ {0xa0, 0x80, ZC3XX_R020_HSYNC_3}, /* 00,20,80,cc */
+ {}
+};
+static const struct usb_action hv7131b_60HZ[] = { /* 640x480*/
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */
+ {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0040}, /* 00,21,40,aa */
+ {0xaa, 0x22, 0x0013}, /* 00,22,13,aa */
+ {0xaa, 0x23, 0x004c}, /* 00,23,4c,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */
+ {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,c3,cc */
+ {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,50,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */
+ {0xa0, 0x13, ZC3XX_R01F_HSYNC_2}, /* 00,1f,13,cc */
+ {0xa0, 0x4c, ZC3XX_R020_HSYNC_3}, /* 00,20,4c,cc */
+ {}
+};
+static const struct usb_action hv7131b_60HZScale[] = { /* 320x240 */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */
+ {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */
+ {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */
+ {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */
+ {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */
+ {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,86,cc */
+ {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,a0,cc */
+ {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */
+ {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */
+ {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */
+ {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */
+ {}
+};
+static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */
+ {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */
+ {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
+ {0xaa, 0x23, 0x0003}, /* 00,23,03,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */
+ {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x10, ZC3XX_R01E_HSYNC_1}, /* 00,1e,10,cc */
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, /* 00,1f,00,cc */
+ {0xa0, 0x03, ZC3XX_R020_HSYNC_3}, /* 00,20,03,cc */
+ {}
+};
+static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */
+ {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */
+ {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */
+ {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */
+ {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */
+ {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */
+ {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */
{}
};
@@ -4389,6 +4130,270 @@ static const struct usb_action pas106b_NoFliker[] = {
{}
};
+/* from usbvm31b.inf */
+static const struct usb_action pas202b_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+ {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,03,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,03,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xaa, 0x02, 0x0002}, /* 00,02,04,aa --> 02 */
+ {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */
+ {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */
+ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
+ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
+ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
+ {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
+ {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
+ {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */
+ {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
+ {}
+};
+static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+ {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,08,cc */
+ {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,02,cc */
+ {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */
+ {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xaa, 0x02, 0x0002}, /* 00,02,02,aa */
+ {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */
+ {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */
+ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
+ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
+ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
+ {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
+ {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
+ {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */
+ {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
+ {}
+};
+static const struct usb_action pas202b_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0068}, /* 00,21,68,aa */
+ {0xaa, 0x03, 0x0044}, /* 00,03,44,aa */
+ {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */
+ {0xaa, 0x05, 0x0028}, /* 00,05,28,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,d2,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
+ {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, /* 00,20,eb,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x006c}, /* 00,21,6c,aa */
+ {0xaa, 0x03, 0x0041}, /* 00,03,41,aa */
+ {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */
+ {0xaa, 0x05, 0x002c}, /* 00,05,2c,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
+ {0xa0, 0xbe, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,be,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x03, 0x0045}, /* 00,03,45,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+ {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,c0,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */
+ {0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */
+ {0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */
+ {0xa0, 0xf5, ZC3XX_R020_HSYNC_3}, /* 00,20,f5,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0004}, /* 00,21,04,aa */
+ {0xaa, 0x03, 0x0042}, /* 00,03,42,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0004}, /* 00,05,04,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
+ {0xa0, 0x9f, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,9f,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0020}, /* 00,21,20,aa */
+ {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0020}, /* 00,05,20,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */
+ {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0010}, /* 00,05,10,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+
static const struct usb_action pb03303x_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
@@ -5725,7 +5730,7 @@ static const struct usb_action tas5130cxx_Initial[] = {
{}
};
static const struct usb_action tas5130cxx_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+/*?? {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
@@ -6049,7 +6054,7 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = {
{0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa, */
{0xaa, 0x13, 0x0002}, /* 00,13,02,aa, */
{0xaa, 0x15, 0x0004}, /* 00,15,04,aa */
- {0xaa, 0x01, 0x0000},
+/*?? {0xaa, 0x01, 0x0000}, */
{0xaa, 0x01, 0x0000},
{0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */
{0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */
@@ -6065,8 +6070,8 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = {
{0xaa, 0x0f, 0x00a0}, /* 00,0f,a0,aa, */
{0xaa, 0x10, 0x0000}, /* 00,10,00,aa, */
{0xaa, 0x11, 0x00a0}, /* 00,11,a0,aa, */
- {0xa0, 0x00, 0x0039},
- {0xa1, 0x01, 0x0037},
+/*?? {0xa0, 0x00, 0x0039},
+ {0xa1, 0x01, 0x0037}, */
{0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */
{0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa (e6 -> e8) */
{0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */
@@ -6303,7 +6308,7 @@ static __u8 i2c_write(struct gspca_dev *gspca_dev,
reg_w_i(gspca_dev->dev, valL, 0x93);
reg_w_i(gspca_dev->dev, valH, 0x94);
reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */
- msleep(5);
+ msleep(15);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)",
reg, valH, valL, retbyte);
@@ -6346,30 +6351,35 @@ static void setmatrix(struct gspca_dev *gspca_dev)
{0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50};
static const __u8 ov7620_matrix[9] =
{0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58};
+ static const __u8 pas202b_matrix[9] =
+ {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f};
static const __u8 po2030_matrix[9] =
{0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60};
static const __u8 vf0250_matrix[9] =
{0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b};
+ static const __u8 *matrix_tb[SENSOR_MAX] = {
+ NULL, /* SENSOR_CS2102 0 */
+ NULL, /* SENSOR_CS2102K 1 */
+ gc0305_matrix, /* SENSOR_GC0305 2 */
+ NULL, /* SENSOR_HDCS2020b 3 */
+ NULL, /* SENSOR_HV7131B 4 */
+ NULL, /* SENSOR_HV7131C 5 */
+ NULL, /* SENSOR_ICM105A 6 */
+ NULL, /* SENSOR_MC501CB 7 */
+ ov7620_matrix, /* SENSOR_OV7620 8 */
+ NULL, /* SENSOR_OV7630C 9 */
+ NULL, /* SENSOR_PAS106 10 */
+ pas202b_matrix, /* SENSOR_PAS202B 11 */
+ NULL, /* SENSOR_PB0330 12 */
+ po2030_matrix, /* SENSOR_PO2030 13 */
+ NULL, /* SENSOR_TAS5130CK 14 */
+ NULL, /* SENSOR_TAS5130CXX 15 */
+ vf0250_matrix, /* SENSOR_TAS5130C_VF0250 16 */
+ };
- switch (sd->sensor) {
- case SENSOR_GC0305:
- matrix = gc0305_matrix;
- break;
- case SENSOR_MC501CB:
- return; /* no matrix? */
- case SENSOR_OV7620:
-/* case SENSOR_OV7648: */
- matrix = ov7620_matrix;
- break;
- case SENSOR_PO2030:
- matrix = po2030_matrix;
- break;
- case SENSOR_TAS5130C_VF0250:
- matrix = vf0250_matrix;
- break;
- default: /* matrix already loaded */
- return;
- }
+ matrix = matrix_tb[sd->sensor];
+ if (matrix == NULL)
+ return; /* matrix already loaded */
for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++)
reg_w(gspca_dev->dev, matrix[i], 0x010a + i);
}
@@ -6585,42 +6595,42 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
{gc0305_NoFliker, gc0305_NoFliker,
gc0305_50HZ, gc0305_50HZ,
gc0305_60HZ, gc0305_60HZ},
-/* SENSOR_HDCS2020 3 */
- {NULL, NULL,
- NULL, NULL,
- NULL, NULL},
-/* SENSOR_HDCS2020b 4 */
+/* SENSOR_HDCS2020b 3 */
{hdcs2020b_NoFliker, hdcs2020b_NoFliker,
hdcs2020b_50HZ, hdcs2020b_50HZ,
hdcs2020b_60HZ, hdcs2020b_60HZ},
-/* SENSOR_HV7131B 5 */
+/* SENSOR_HV7131B 4 */
+ {hv7131b_NoFlikerScale, hv7131b_NoFliker,
+ hv7131b_50HZScale, hv7131b_50HZ,
+ hv7131b_60HZScale, hv7131b_60HZ},
+/* SENSOR_HV7131C 5 */
{NULL, NULL,
NULL, NULL,
NULL, NULL},
-/* SENSOR_HV7131C 6 */
- {NULL, NULL,
- NULL, NULL,
- NULL, NULL},
-/* SENSOR_ICM105A 7 */
+/* SENSOR_ICM105A 6 */
{icm105a_NoFliker, icm105a_NoFlikerScale,
icm105a_50HZ, icm105a_50HZScale,
icm105a_60HZ, icm105a_60HZScale},
-/* SENSOR_MC501CB 8 */
+/* SENSOR_MC501CB 7 */
{MC501CB_NoFliker, MC501CB_NoFlikerScale,
MC501CB_50HZ, MC501CB_50HZScale,
MC501CB_60HZ, MC501CB_60HZScale},
-/* SENSOR_OV7620 9 */
+/* SENSOR_OV7620 8 */
{OV7620_NoFliker, OV7620_NoFliker,
OV7620_50HZ, OV7620_50HZ,
OV7620_60HZ, OV7620_60HZ},
-/* SENSOR_OV7630C 10 */
+/* SENSOR_OV7630C 9 */
{NULL, NULL,
NULL, NULL,
NULL, NULL},
-/* SENSOR_PAS106 11 */
+/* SENSOR_PAS106 10 */
{pas106b_NoFliker, pas106b_NoFliker,
pas106b_50HZ, pas106b_50HZ,
pas106b_60HZ, pas106b_60HZ},
+/* SENSOR_PAS202B 11 */
+ {pas202b_NoFlikerScale, pas202b_NoFliker,
+ pas202b_50HZScale, pas202b_50HZ,
+ pas202b_60HZScale, pas202b_60HZ},
/* SENSOR_PB0330 12 */
{pb0330_NoFliker, pb0330_NoFlikerScale,
pb0330_50HZ, pb0330_50HZScale,
@@ -7002,15 +7012,15 @@ static int sd_config(struct gspca_dev *gspca_dev,
5, /* SENSOR_CS2102 0 */
5, /* SENSOR_CS2102K 1 */
4, /* SENSOR_GC0305 2 */
- 4, /* SENSOR_HDCS2020 3 */
- 4, /* SENSOR_HDCS2020b 4 */
- 4, /* SENSOR_HV7131B 5 */
- 4, /* SENSOR_HV7131C 6 */
- 4, /* SENSOR_ICM105A 7 */
- 4, /* SENSOR_MC501CB 8 */
- 3, /* SENSOR_OV7620 9 */
- 4, /* SENSOR_OV7630C 10 */
- 4, /* SENSOR_PAS106 11 */
+ 4, /* SENSOR_HDCS2020b 3 */
+ 4, /* SENSOR_HV7131B 4 */
+ 4, /* SENSOR_HV7131C 5 */
+ 4, /* SENSOR_ICM105A 6 */
+ 4, /* SENSOR_MC501CB 7 */
+ 3, /* SENSOR_OV7620 8 */
+ 4, /* SENSOR_OV7630C 9 */
+ 4, /* SENSOR_PAS106 10 */
+ 4, /* SENSOR_PAS202B 11 */
4, /* SENSOR_PB0330 12 */
4, /* SENSOR_PO2030 13 */
4, /* SENSOR_TAS5130CK 14 */
@@ -7066,8 +7076,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor = SENSOR_ICM105A;
break;
case 0x0e:
- PDEBUG(D_PROBE, "Find Sensor HDCS2020");
- sd->sensor = SENSOR_HDCS2020;
+ PDEBUG(D_PROBE, "Find Sensor PAS202B");
+ sd->sensor = SENSOR_PAS202B;
sd->sharpness = 1;
break;
case 0x0f:
@@ -7153,7 +7163,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->gamma = gamma[(int) sd->sensor];
sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value;
sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value;
- sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value;
switch (sd->sensor) {
case SENSOR_GC0305:
@@ -7161,7 +7170,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
case SENSOR_PO2030:
gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX);
break;
- case SENSOR_HDCS2020:
case SENSOR_HV7131B:
case SENSOR_HV7131C:
case SENSOR_OV7630C:
@@ -7191,15 +7199,15 @@ static int sd_start(struct gspca_dev *gspca_dev)
{cs2102_InitialScale, cs2102_Initial}, /* 0 */
{cs2102K_InitialScale, cs2102K_Initial}, /* 1 */
{gc0305_Initial, gc0305_InitialScale}, /* 2 */
- {hdcs2020xx_InitialScale, hdcs2020xx_Initial}, /* 3 */
- {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 4 */
- {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 5 */
- {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */
- {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */
- {MC501CB_InitialScale, MC501CB_Initial}, /* 9 */
- {OV7620_mode0, OV7620_mode1}, /* 9 */
- {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */
- {pas106b_InitialScale, pas106b_Initial}, /* 11 */
+ {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 3 */
+ {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 4 */
+ {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 5 */
+ {icm105axx_InitialScale, icm105axx_Initial}, /* 6 */
+ {MC501CB_InitialScale, MC501CB_Initial}, /* 7 */
+ {OV7620_mode0, OV7620_mode1}, /* 8 */
+ {ov7630c_InitialScale, ov7630c_Initial}, /* 9 */
+ {pas106b_InitialScale, pas106b_Initial}, /* 10 */
+ {pas202b_Initial, pas202b_InitialScale}, /* 11 */
{pb0330xx_InitialScale, pb0330xx_Initial}, /* 12 */
/* or {pb03303x_InitialScale, pb03303x_Initial}, */
{PO2030_mode0, PO2030_mode1}, /* 13 */
@@ -7256,6 +7264,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_r(gspca_dev, 0x0008);
reg_w(dev, 0x00, 0x0008);
break;
+ case SENSOR_PAS202B:
case SENSOR_GC0305:
reg_r(gspca_dev, 0x0008);
/* fall thru */
@@ -7269,7 +7278,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_CS2102: /* gamma set in xxx_Initial */
case SENSOR_CS2102K:
- case SENSOR_HDCS2020:
case SENSOR_HDCS2020b:
case SENSOR_PB0330: /* pb with chip_revision - see above */
case SENSOR_OV7630C:
@@ -7282,6 +7290,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
setmatrix(gspca_dev); /* one more time? */
switch (sd->sensor) {
case SENSOR_OV7620:
+ case SENSOR_PAS202B:
reg_r(gspca_dev, 0x0180); /* from win */
reg_w(dev, 0x00, 0x0180);
break;
@@ -7293,37 +7302,29 @@ static int sd_start(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_GC0305:
- case SENSOR_OV7620:
reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
reg_w(dev, 0x15, 0x01ae);
- sd->autogain = 0;
- break;
+ /* fall thru */
+ case SENSOR_PAS202B:
case SENSOR_PO2030:
- reg_w(dev, 0x40, 0x0117); /* (from win traces) */
+/* reg_w(dev, 0x40, ZC3XX_R117_GGAIN); * (from win traces) */
reg_r(gspca_dev, 0x0180);
break;
- }
-
- setautogain(gspca_dev);
- switch (sd->sensor) {
- case SENSOR_GC0305:
-/* setlightfreq(gspca_dev); ?? (end: 80 -> [18d]) */
- reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
- reg_w(dev, 0x15, 0x01ae);
- reg_w(dev, 0x40, 0x0180);
- reg_w(dev, 0x40, 0x0117);
- reg_r(gspca_dev, 0x0180);
- sd->autogain = 1;
- setautogain(gspca_dev);
- break;
case SENSOR_OV7620:
+ reg_w(dev, 0x09, 0x01ad);
+ reg_w(dev, 0x15, 0x01ae);
i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */
i2c_write(gspca_dev, 0x13, 0xa3, 0x00);
/*fixme: returned value to send? */
- reg_w(dev, 0x40, 0x0117); /* (from win traces) */
+ reg_w(dev, 0x40, 0x0117);
reg_r(gspca_dev, 0x0180);
- setautogain(gspca_dev);
- msleep(500);
+ break;
+ }
+
+ setautogain(gspca_dev);
+ switch (sd->sensor) {
+ case SENSOR_PAS202B:
+ reg_w(dev, 0x00, 0x0007); /* (from win traces) */
break;
case SENSOR_PO2030:
msleep(500);
@@ -7333,6 +7334,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(dev, 0x02, 0x0008);
break;
}
+ if (sd->sensor == SENSOR_PAS202B)
+ reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING);
return 0;
}
@@ -7530,6 +7533,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x0458, 0x700c)},
{USB_DEVICE(0x0458, 0x700f)},
{USB_DEVICE(0x0461, 0x0a00)},
+ {USB_DEVICE(0x046d, 0x089d), .driver_info = SENSOR_MC501CB},
{USB_DEVICE(0x046d, 0x08a0)},
{USB_DEVICE(0x046d, 0x08a1)},
{USB_DEVICE(0x046d, 0x08a2)},
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index efe849981ab7..d4658c56eddc 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -385,10 +385,10 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
goto err_out_detach;
}
- /* Phys addr can only be set after attaching (for ir->c.dev.bus_id) */
+ /* Phys addr can only be set after attaching (for ir->c.dev) */
snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0",
- ir->c.adapter->dev.bus_id,
- ir->c.dev.bus_id);
+ dev_name(&ir->c.adapter->dev),
+ dev_name(&ir->c.dev));
/* init + register input device */
ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes);
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c
index 4e05f91a9100..2883c8780760 100644
--- a/drivers/media/video/ivtv/ivtv-cards.c
+++ b/drivers/media/video/ivtv/ivtv-cards.c
@@ -877,20 +877,28 @@ static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = {
static const struct ivtv_card ivtv_card_pg600v2 = {
.type = IVTV_CARD_PG600V2,
.name = "Yuan PG600-2, GotView PCI DVD Lite",
- .comment = "only Composite and S-Video inputs are supported, not the tuner\n",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
.hw_audio_ctrl = IVTV_HW_CX25840,
- .hw_all = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ /* XC2028 support apparently works for the Yuan, it's still
+ uncertain whether it also works with the GotView. */
.video_inputs = {
- { IVTV_CARD_INPUT_SVIDEO1, 0,
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
- { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
},
.audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
},
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ .xceive_pin = 12,
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
.pci_list = ivtv_pci_pg600v2,
.i2c = &ivtv_i2c_std,
};
diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c
index 48e103be7183..62aa06f5d168 100644
--- a/drivers/media/video/ivtv/ivtv-controls.c
+++ b/drivers/media/video/ivtv/ivtv-controls.c
@@ -63,7 +63,7 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
- if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl))
+ if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
@@ -73,7 +73,7 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
- if (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
+ if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
@@ -122,7 +122,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
- return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl);
+ return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl);
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
@@ -130,7 +130,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
- return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
+ return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl);
default:
IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
@@ -147,7 +147,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
- return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl);
+ return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl);
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
@@ -155,7 +155,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
- return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
+ return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl);
default:
IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
return -EINVAL;
@@ -268,7 +268,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1);
fmt.fmt.pix.height = itv->params.height;
- itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, &fmt);
}
err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt)
@@ -279,7 +279,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
/* The audio clock of the digitizer must match the codec sample
rate otherwise you get some very strange effects. */
if (idx < sizeof(freqs))
- ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[idx]);
+ ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
return err;
}
return -EINVAL;
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index b69cc1d55e5b..08b762951759 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -60,9 +60,6 @@
#include <media/v4l2-chip-ident.h>
#include "tuner-xc2028.h"
-/* var to keep track of the number of array elements in use */
-int ivtv_cards_active;
-
/* If you have already X v4l cards, then set this to X. This way
the device numbers stay matched. Example: you have a WinTV card
without radio and a PVR-350 with. Normally this would give a
@@ -70,12 +67,6 @@ int ivtv_cards_active;
setting this to 1 you ensure that radio0 is now also radio1. */
int ivtv_first_minor;
-/* Master variable for all ivtv info */
-struct ivtv *ivtv_cards[IVTV_MAX_CARDS];
-
-/* Protects ivtv_cards_active */
-DEFINE_SPINLOCK(ivtv_cards_lock);
-
/* add your revision and whatnot here */
static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
{PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
@@ -87,6 +78,9 @@ static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl);
+/* ivtv instance counter */
+static atomic_t ivtv_instance = ATOMIC_INIT(0);
+
/* Parameter declarations */
static int cardtype[IVTV_MAX_CARDS];
static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
@@ -599,9 +593,9 @@ static void ivtv_process_options(struct ivtv *itv)
itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers * 1024;
itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers * 1024;
itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers;
- itv->options.cardtype = cardtype[itv->num];
- itv->options.tuner = tuner[itv->num];
- itv->options.radio = radio[itv->num];
+ itv->options.cardtype = cardtype[itv->instance];
+ itv->options.tuner = tuner[itv->instance];
+ itv->options.radio = radio[itv->instance];
itv->options.newi2c = newi2c;
if (tunertype < -1 || tunertype > 1) {
IVTV_WARN("Invalid tunertype argument, will autodetect instead\n");
@@ -688,7 +682,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv)
spin_lock_init(&itv->lock);
spin_lock_init(&itv->dma_reg_lock);
- itv->irq_work_queues = create_singlethread_workqueue(itv->name);
+ itv->irq_work_queues = create_singlethread_workqueue(itv->device.name);
if (itv->irq_work_queues == NULL) {
IVTV_ERR("Could not create ivtv workqueue\n");
return -1;
@@ -770,12 +764,6 @@ static void __devinit ivtv_init_struct2(struct ivtv *itv)
i = 0;
itv->active_input = i;
itv->audio_input = itv->card->video_inputs[i].audio_index;
- if (itv->card->hw_all & IVTV_HW_CX25840)
- itv->video_dec_func = ivtv_cx25840;
- else if (itv->card->hw_all & IVTV_HW_SAA717X)
- itv->video_dec_func = ivtv_saa717x;
- else
- itv->video_dec_func = ivtv_saa7115;
}
static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
@@ -788,21 +776,21 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
IVTV_DEBUG_INFO("Enabling pci device\n");
if (pci_enable_device(dev)) {
- IVTV_ERR("Can't enable device %d!\n", itv->num);
+ IVTV_ERR("Can't enable device!\n");
return -EIO;
}
if (pci_set_dma_mask(dev, 0xffffffff)) {
- IVTV_ERR("No suitable DMA available on card %d.\n", itv->num);
+ IVTV_ERR("No suitable DMA available.\n");
return -EIO;
}
if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) {
- IVTV_ERR("Cannot request encoder memory region on card %d.\n", itv->num);
+ IVTV_ERR("Cannot request encoder memory region.\n");
return -EIO;
}
if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET,
IVTV_REG_SIZE, "ivtv registers")) {
- IVTV_ERR("Cannot request register memory region on card %d.\n", itv->num);
+ IVTV_ERR("Cannot request register memory region.\n");
release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
return -EIO;
}
@@ -810,7 +798,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
if (itv->has_cx23415 &&
!request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET,
IVTV_DECODER_SIZE, "ivtv decoder")) {
- IVTV_ERR("Cannot request decoder memory region on card %d.\n", itv->num);
+ IVTV_ERR("Cannot request decoder memory region.\n");
release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
return -EIO;
@@ -853,69 +841,11 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
return 0;
}
-#ifdef MODULE
-static u32 ivtv_request_module(struct ivtv *itv, u32 hw,
- const char *name, u32 id)
-{
- if ((hw & id) == 0)
- return hw;
- if (request_module(name) != 0) {
- IVTV_ERR("Failed to load module %s\n", name);
- return hw & ~id;
- }
- IVTV_DEBUG_INFO("Loaded module %s\n", name);
- return hw;
-}
-#endif
-
static void ivtv_load_and_init_modules(struct ivtv *itv)
{
u32 hw = itv->card->hw_all;
unsigned i;
-#ifdef MODULE
- /* load modules */
-#ifdef CONFIG_MEDIA_TUNER_MODULE
- hw = ivtv_request_module(itv, hw, "tuner", IVTV_HW_TUNER);
-#endif
-#ifdef CONFIG_VIDEO_CX25840_MODULE
- hw = ivtv_request_module(itv, hw, "cx25840", IVTV_HW_CX25840);
-#endif
-#ifdef CONFIG_VIDEO_SAA711X_MODULE
- hw = ivtv_request_module(itv, hw, "saa7115", IVTV_HW_SAA711X);
-#endif
-#ifdef CONFIG_VIDEO_SAA7127_MODULE
- hw = ivtv_request_module(itv, hw, "saa7127", IVTV_HW_SAA7127);
-#endif
-#ifdef CONFIG_VIDEO_SAA717X_MODULE
- hw = ivtv_request_module(itv, hw, "saa717x", IVTV_HW_SAA717X);
-#endif
-#ifdef CONFIG_VIDEO_UPD64031A_MODULE
- hw = ivtv_request_module(itv, hw, "upd64031a", IVTV_HW_UPD64031A);
-#endif
-#ifdef CONFIG_VIDEO_UPD64083_MODULE
- hw = ivtv_request_module(itv, hw, "upd64083", IVTV_HW_UPD6408X);
-#endif
-#ifdef CONFIG_VIDEO_MSP3400_MODULE
- hw = ivtv_request_module(itv, hw, "msp3400", IVTV_HW_MSP34XX);
-#endif
-#ifdef CONFIG_VIDEO_VP27SMPX_MODULE
- hw = ivtv_request_module(itv, hw, "vp27smpx", IVTV_HW_VP27SMPX);
-#endif
-#ifdef CONFIG_VIDEO_WM8775_MODULE
- hw = ivtv_request_module(itv, hw, "wm8775", IVTV_HW_WM8775);
-#endif
-#ifdef CONFIG_VIDEO_WM8739_MODULE
- hw = ivtv_request_module(itv, hw, "wm8739", IVTV_HW_WM8739);
-#endif
-#ifdef CONFIG_VIDEO_CS53L32A_MODULE
- hw = ivtv_request_module(itv, hw, "cs53l32a", IVTV_HW_CS53L32A);
-#endif
-#ifdef CONFIG_VIDEO_M52790_MODULE
- hw = ivtv_request_module(itv, hw, "m52790", IVTV_HW_M52790);
-#endif
-#endif
-
/* check which i2c devices are actually found */
for (i = 0; i < 32; i++) {
u32 device = 1 << i;
@@ -927,11 +857,21 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
itv->hw_flags |= device;
continue;
}
- ivtv_i2c_register(itv, i);
- if (ivtv_i2c_hw_addr(itv, device) > 0)
+ if (ivtv_i2c_register(itv, i) == 0)
itv->hw_flags |= device;
}
+ if (itv->card->hw_all & IVTV_HW_CX25840)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840);
+ else if (itv->card->hw_all & IVTV_HW_SAA717X)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA717X);
+ else if (itv->card->hw_all & IVTV_HW_SAA7114)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7114);
+ else
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7115);
+ itv->sd_audio = ivtv_find_hw(itv, itv->card->hw_audio_ctrl);
+ itv->sd_muxer = ivtv_find_hw(itv, itv->card->hw_muxer);
+
hw = itv->hw_flags;
if (itv->card->type == IVTV_CARD_CX23416GYC) {
@@ -949,7 +889,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
/* The crystal frequency of GVMVPRX is 24.576MHz */
crystal_freq.freq = SAA7115_FREQ_24_576_MHZ;
crystal_freq.flags = SAA7115_FREQ_FL_UCGC;
- itv->video_dec_func(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq);
+ v4l2_subdev_call(itv->sd_video, video, s_crystal_freq, &crystal_freq);
}
if (hw & IVTV_HW_CX25840) {
@@ -967,7 +907,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
/* determine the exact saa711x model */
itv->hw_flags &= ~IVTV_HW_SAA711X;
- ivtv_saa7115(itv, VIDIOC_G_CHIP_IDENT, &v);
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v);
if (v.ident == V4L2_IDENT_SAA7114) {
itv->hw_flags |= IVTV_HW_SAA7114;
/* VBI is not yet supported by the saa7114 driver. */
@@ -1001,28 +941,20 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
int vbi_buf_size;
struct ivtv *itv;
- spin_lock(&ivtv_cards_lock);
-
- /* Make sure we've got a place for this card */
- if (ivtv_cards_active == IVTV_MAX_CARDS) {
- printk(KERN_ERR "ivtv: Maximum number of cards detected (%d)\n",
- ivtv_cards_active);
- spin_unlock(&ivtv_cards_lock);
- return -ENOMEM;
- }
-
itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC);
- if (itv == NULL) {
- spin_unlock(&ivtv_cards_lock);
+ if (itv == NULL)
return -ENOMEM;
- }
- ivtv_cards[ivtv_cards_active] = itv;
itv->dev = dev;
- itv->num = ivtv_cards_active++;
- snprintf(itv->name, sizeof(itv->name), "ivtv%d", itv->num);
- IVTV_INFO("Initializing card #%d\n", itv->num);
+ itv->instance = atomic_inc_return(&ivtv_instance) - 1;
- spin_unlock(&ivtv_cards_lock);
+ retval = v4l2_device_register(&dev->dev, &itv->device);
+ if (retval)
+ return retval;
+ /* "ivtv + PCI ID" is a bit of a mouthful, so use
+ "ivtv + instance" instead. */
+ snprintf(itv->device.name, sizeof(itv->device.name),
+ "ivtv%d", itv->instance);
+ IVTV_INFO("Initializing card %d\n", itv->instance);
ivtv_process_options(itv);
if (itv->options.cardtype == -1) {
@@ -1043,8 +975,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
else if (retval == -ENXIO)
goto free_mem;
}
- /* save itv in the pci struct for later use */
- pci_set_drvdata(dev, itv);
/* map io memory */
IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
@@ -1086,7 +1016,9 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
goto free_io;
}
- ivtv_gpio_init(itv);
+ retval = ivtv_gpio_init(itv);
+ if (retval)
+ goto free_io;
/* active i2c */
IVTV_DEBUG_INFO("activating i2c...\n");
@@ -1095,8 +1027,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
goto free_io;
}
- IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active);
-
if (itv->card->hw_all & IVTV_HW_TVEEPROM) {
/* Based on the model number the cardtype may be changed.
The PCI IDs are not always reliable. */
@@ -1191,7 +1121,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
setup.tuner_callback = (setup.type == TUNER_XC2028) ?
ivtv_reset_tuner_gpio : NULL;
- ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup);
+ ivtv_call_all(itv, tuner, s_type_addr, &setup);
if (setup.type == TUNER_XC2028) {
static struct xc2028_ctrl ctrl = {
.fname = XC2028_DEFAULT_FIRMWARE,
@@ -1201,7 +1131,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
.tuner = itv->options.tuner,
.priv = &ctrl,
};
- ivtv_call_i2c_clients(itv, TUNER_SET_CONFIG, &cfg);
+ ivtv_call_all(itv, tuner, s_config, &cfg);
}
}
@@ -1210,11 +1140,11 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
itv->tuner_std = itv->std;
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
- ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std);
+ ivtv_call_all(itv, video, s_std_output, itv->std);
/* Turn off the output signal. The mpeg decoder is not yet
active so without this you would get a green image until the
mpeg decoder becomes active. */
- ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
}
/* clear interrupt mask, effectively disabling interrupts */
@@ -1222,7 +1152,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
/* Register IRQ */
retval = request_irq(itv->dev->irq, ivtv_irq_handler,
- IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv);
+ IRQF_SHARED | IRQF_DISABLED, itv->device.name, (void *)itv);
if (retval) {
IVTV_ERR("Failed to register irq %d\n", retval);
goto free_i2c;
@@ -1238,7 +1168,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
IVTV_ERR("Error %d registering devices\n", retval);
goto free_streams;
}
- IVTV_INFO("Initialized card #%d: %s\n", itv->num, itv->card_name);
+ IVTV_INFO("Initialized card: %s\n", itv->card_name);
return 0;
free_streams:
@@ -1261,10 +1191,8 @@ err:
retval = -ENODEV;
IVTV_ERR("Error %d on initialization\n", retval);
- spin_lock(&ivtv_cards_lock);
- kfree(ivtv_cards[ivtv_cards_active]);
- ivtv_cards[ivtv_cards_active] = NULL;
- spin_unlock(&ivtv_cards_lock);
+ v4l2_device_unregister(&itv->device);
+ kfree(itv);
return retval;
}
@@ -1304,10 +1232,11 @@ int ivtv_init_on_first_open(struct ivtv *itv)
if (itv->card->hw_all & IVTV_HW_CX25840) {
struct v4l2_control ctrl;
+ v4l2_subdev_call(itv->sd_video, core, init, 0);
/* CX25840_CID_ENABLE_PVR150_WORKAROUND */
ctrl.id = V4L2_CID_PRIVATE_BASE;
ctrl.value = itv->pvr150_workaround;
- itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl);
+ v4l2_subdev_call(itv->sd_video, core, s_ctrl, &ctrl);
}
vf.tuner = 0;
@@ -1337,7 +1266,7 @@ int ivtv_init_on_first_open(struct ivtv *itv)
/* Turn on the TV-out: ivtv_init_mpeg_decoder() initializes
the mpeg decoder so now the saa7127 receives a proper
signal. */
- ivtv_saa7127(itv, VIDIOC_STREAMON, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
ivtv_init_mpeg_decoder(itv);
}
ivtv_s_std(NULL, &fh, &itv->tuner_std);
@@ -1362,9 +1291,11 @@ int ivtv_init_on_first_open(struct ivtv *itv)
static void ivtv_remove(struct pci_dev *pci_dev)
{
- struct ivtv *itv = pci_get_drvdata(pci_dev);
+ struct v4l2_device *dev = dev_get_drvdata(&pci_dev->dev);
+ struct ivtv *itv = to_ivtv(dev);
+ int i;
- IVTV_DEBUG_INFO("Removing Card #%d\n", itv->num);
+ IVTV_DEBUG_INFO("Removing card\n");
if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) {
/* Stop all captures */
@@ -1377,7 +1308,7 @@ static void ivtv_remove(struct pci_dev *pci_dev)
/* Turn off the TV-out */
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
- ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
if (atomic_read(&itv->decoding) > 0) {
int type;
@@ -1402,6 +1333,8 @@ static void ivtv_remove(struct pci_dev *pci_dev)
ivtv_streams_cleanup(itv, 1);
ivtv_udma_free(itv);
+ v4l2_device_unregister(&itv->device);
+
exit_ivtv_i2c(itv);
free_irq(itv->dev->irq, (void *)itv);
@@ -1413,8 +1346,11 @@ static void ivtv_remove(struct pci_dev *pci_dev)
release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
pci_disable_device(itv->dev);
+ for (i = 0; i < IVTV_VBI_FRAMES; i++)
+ kfree(itv->vbi.sliced_mpeg_data[i]);
- IVTV_INFO("Removed %s, card #%d\n", itv->card_name, itv->num);
+ printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name);
+ kfree(itv);
}
/* define a pci_driver for card detection */
@@ -1427,54 +1363,36 @@ static struct pci_driver ivtv_pci_driver = {
static int module_start(void)
{
- printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION);
-
- memset(ivtv_cards, 0, sizeof(ivtv_cards));
+ printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION);
/* Validate parameters */
if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) {
- printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n",
+ printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n",
IVTV_MAX_CARDS - 1);
return -1;
}
if (ivtv_debug < 0 || ivtv_debug > 2047) {
ivtv_debug = 0;
- printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n");
+ printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n");
}
if (pci_register_driver(&ivtv_pci_driver)) {
- printk(KERN_ERR "ivtv: Error detecting PCI card\n");
+ printk(KERN_ERR "ivtv: Error detecting PCI card\n");
return -ENODEV;
}
- printk(KERN_INFO "ivtv: End initialization\n");
+ printk(KERN_INFO "ivtv: End initialization\n");
return 0;
}
static void module_cleanup(void)
{
- int i, j;
-
pci_unregister_driver(&ivtv_pci_driver);
-
- spin_lock(&ivtv_cards_lock);
- for (i = 0; i < ivtv_cards_active; i++) {
- if (ivtv_cards[i] == NULL)
- continue;
- for (j = 0; j < IVTV_VBI_FRAMES; j++) {
- kfree(ivtv_cards[i]->vbi.sliced_mpeg_data[j]);
- }
- kfree(ivtv_cards[i]);
- }
- spin_unlock(&ivtv_cards_lock);
}
/* Note: These symbols are exported because they are used by the ivtvfb
framebuffer module and an infrared module for the IR-blaster. */
EXPORT_SYMBOL(ivtv_set_irq_mask);
-EXPORT_SYMBOL(ivtv_cards_active);
-EXPORT_SYMBOL(ivtv_cards);
-EXPORT_SYMBOL(ivtv_cards_lock);
EXPORT_SYMBOL(ivtv_api);
EXPORT_SYMBOL(ivtv_vapi);
EXPORT_SYMBOL(ivtv_vapi_result);
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index 3733b2afec5f..ce8d9b74357e 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -61,6 +61,7 @@
#include <linux/dvb/audio.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
#include <media/tuner.h>
#include <media/cx2341x.h>
@@ -113,9 +114,6 @@
#define IVTV_REG_VPU (0x9058)
#define IVTV_REG_APU (0xA064)
-/* i2c stuff */
-#define I2C_CLIENTS_MAX 16
-
/* debugging */
extern int ivtv_debug;
@@ -132,12 +130,10 @@ extern int ivtv_debug;
/* Flag to turn on high volume debugging */
#define IVTV_DBGFLG_HIGHVOL (1 << 10)
-/* NOTE: extra space before comma in 'itv->num , ## args' is required for
- gcc-2.95, otherwise it won't compile. */
#define IVTV_DEBUG(x, type, fmt, args...) \
do { \
if ((x) & ivtv_debug) \
- printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \
+ v4l2_info(&itv->device, " " type ": " fmt , ##args); \
} while (0)
#define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warn", fmt , ## args)
#define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args)
@@ -152,8 +148,8 @@ extern int ivtv_debug;
#define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \
do { \
- if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \
- printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \
+ if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \
+ v4l2_info(&itv->device, " " type ": " fmt , ##args); \
} while (0)
#define IVTV_DEBUG_HI_WARN(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN, "warn", fmt , ## args)
#define IVTV_DEBUG_HI_INFO(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO, "info", fmt , ## args)
@@ -167,9 +163,9 @@ extern int ivtv_debug;
#define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
/* Standard kernel messages */
-#define IVTV_ERR(fmt, args...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args)
-#define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args)
-#define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args)
+#define IVTV_ERR(fmt, args...) v4l2_err(&itv->device, fmt , ## args)
+#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->device, fmt , ## args)
+#define IVTV_INFO(fmt, args...) v4l2_info(&itv->device, fmt , ## args)
/* output modes (cx23415 only) */
#define OUT_NONE 0
@@ -596,8 +592,6 @@ struct ivtv_card;
/* Struct to hold info about ivtv cards */
struct ivtv {
/* General fixed card data */
- int num; /* board number, -1 during init! */
- char name[8]; /* board name for printk and interrupts (e.g. 'ivtv0') */
struct pci_dev *dev; /* PCI device */
const struct ivtv_card *card; /* card information */
const char *card_name; /* full name of the card */
@@ -609,14 +603,18 @@ struct ivtv {
u32 v4l2_cap; /* V4L2 capabilities of card */
u32 hw_flags; /* hardware description of the board */
v4l2_std_id tuner_std; /* the norm of the card's tuner (fixed) */
- /* controlling video decoder function */
- int (*video_dec_func)(struct ivtv *, unsigned int, void *);
+ struct v4l2_subdev *sd_video; /* controlling video decoder subdev */
+ struct v4l2_subdev *sd_audio; /* controlling audio subdev */
+ struct v4l2_subdev *sd_muxer; /* controlling audio muxer subdev */
u32 base_addr; /* PCI resource base address */
volatile void __iomem *enc_mem; /* pointer to mapped encoder memory */
volatile void __iomem *dec_mem; /* pointer to mapped decoder memory */
volatile void __iomem *reg_mem; /* pointer to mapped registers */
struct ivtv_options options; /* user options */
+ struct v4l2_device device;
+ struct v4l2_subdev sd_gpio; /* GPIO sub-device */
+ u16 instance;
/* High-level state info */
unsigned long i_flags; /* global ivtv flags */
@@ -676,7 +674,6 @@ struct ivtv {
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
- struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];/* pointers to all I2C clients */
int i2c_state; /* i2c bit state */
struct mutex i2c_bus_lock; /* lock i2c bus */
@@ -722,11 +719,13 @@ struct ivtv {
struct osd_info *osd_info; /* ivtvfb private OSD info */
};
+static inline struct ivtv *to_ivtv(struct v4l2_device *dev)
+{
+ return container_of(dev, struct ivtv, device);
+}
+
/* Globals */
-extern struct ivtv *ivtv_cards[];
-extern int ivtv_cards_active;
extern int ivtv_first_minor;
-extern spinlock_t ivtv_cards_lock;
/*==============Prototypes==================*/
@@ -786,4 +785,19 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv)
#define write_dec_sync(val, addr) \
do { write_dec(val, addr); read_dec(addr); } while (0)
+/* Call the specified callback for all subdevs matching hw (if 0, then
+ match them all). Ignore any errors. */
+#define ivtv_call_hw(itv, hw, o, f, args...) \
+ __v4l2_device_call_subdevs(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+
+#define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args)
+
+/* Call the specified callback for all subdevs matching hw (if 0, then
+ match them all). If the callback returns an error other than 0 or
+ -ENOIOCTLCMD, then return with that error code. */
+#define ivtv_call_hw_err(itv, hw, o, f, args...) \
+ __v4l2_device_call_subdevs_until_err(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+
+#define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args)
+
#endif
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
index 1c404e454a36..5eb587592e9d 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -155,7 +155,7 @@ static void ivtv_dualwatch(struct ivtv *itv)
new_stereo_mode = itv->params.audio_properties & stereo_mask;
memset(&vt, 0, sizeof(vt));
- ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt);
+ ivtv_call_all(itv, tuner, g_tuner, &vt);
if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
new_stereo_mode = dual;
@@ -857,7 +857,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp)
/* Mark that the radio is no longer in use */
clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
/* Switch tuner to TV */
- ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+ ivtv_call_all(itv, tuner, s_std, itv->std);
/* Select correct audio input (i.e. TV tuner or Line in) */
ivtv_audio_set_io(itv);
if (itv->hw_flags & IVTV_HW_SAA711X)
@@ -865,7 +865,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp)
struct v4l2_crystal_freq crystal_freq;
crystal_freq.freq = SAA7115_FREQ_32_11_MHZ;
crystal_freq.flags = 0;
- ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq);
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq);
}
if (atomic_read(&itv->capturing) > 0) {
/* Undo video mute */
@@ -952,15 +952,14 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
/* We have the radio */
ivtv_mute(itv);
/* Switch tuner to radio */
- ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL);
+ ivtv_call_all(itv, tuner, s_radio);
/* Select the correct audio input (i.e. radio tuner) */
ivtv_audio_set_io(itv);
- if (itv->hw_flags & IVTV_HW_SAA711X)
- {
+ if (itv->hw_flags & IVTV_HW_SAA711X) {
struct v4l2_crystal_freq crystal_freq;
crystal_freq.freq = SAA7115_FREQ_32_11_MHZ;
crystal_freq.flags = SAA7115_FREQ_FL_APLL;
- ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq);
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq);
}
/* Done! Unmute and continue. */
ivtv_unmute(itv);
@@ -981,37 +980,18 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
int ivtv_v4l2_open(struct inode *inode, struct file *filp)
{
- int res, x, y = 0;
+ int res;
struct ivtv *itv = NULL;
struct ivtv_stream *s = NULL;
- int minor = iminor(inode);
-
- /* Find which card this open was on */
- spin_lock(&ivtv_cards_lock);
- for (x = 0; itv == NULL && x < ivtv_cards_active; x++) {
- if (ivtv_cards[x] == NULL)
- continue;
- /* find out which stream this open was on */
- for (y = 0; y < IVTV_MAX_STREAMS; y++) {
- s = &ivtv_cards[x]->streams[y];
- if (s->v4l2dev && s->v4l2dev->minor == minor) {
- itv = ivtv_cards[x];
- break;
- }
- }
- }
- spin_unlock(&ivtv_cards_lock);
+ struct video_device *vdev = video_devdata(filp);
- if (itv == NULL) {
- /* Couldn't find a device registered
- on that minor, shouldn't happen! */
- printk(KERN_WARNING "No ivtv device found on minor %d\n", minor);
- return -ENXIO;
- }
+ s = video_get_drvdata(vdev);
+ itv = s->itv;
mutex_lock(&itv->serialize_lock);
if (ivtv_init_on_first_open(itv)) {
- IVTV_ERR("Failed to initialize on minor %d\n", minor);
+ IVTV_ERR("Failed to initialize on minor %d\n",
+ s->v4l2dev->minor);
mutex_unlock(&itv->serialize_lock);
return -ENXIO;
}
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
index 74a44844ccaf..dc2850e87a7e 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -144,22 +144,9 @@ int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value)
return 0;
}
-void ivtv_gpio_init(struct ivtv *itv)
+static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd)
{
- u16 pin = 0;
-
- if (itv->card->xceive_pin)
- pin = 1 << itv->card->xceive_pin;
-
- if ((itv->card->gpio_init.direction | pin) == 0)
- return;
-
- IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
- read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
-
- /* init output data then direction */
- write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT);
- write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR);
+ return container_of(sd, struct ivtv, sd_gpio);
}
static struct v4l2_queryctrl gpio_ctrl_mute = {
@@ -173,134 +160,231 @@ static struct v4l2_queryctrl gpio_ctrl_mute = {
.flags = 0,
};
-int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg)
+static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
- struct v4l2_tuner *tuner = arg;
- struct v4l2_control *ctrl = arg;
- struct v4l2_routing *route = arg;
+ struct ivtv *itv = sd_to_ivtv(sd);
u16 mask, data;
- switch (command) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- mask = itv->card->gpio_audio_freq.mask;
- switch (*(u32 *)arg) {
- case 32000:
- data = itv->card->gpio_audio_freq.f32000;
- break;
- case 44100:
- data = itv->card->gpio_audio_freq.f44100;
- break;
- case 48000:
- default:
- data = itv->card->gpio_audio_freq.f48000;
- break;
- }
+ mask = itv->card->gpio_audio_freq.mask;
+ switch (freq) {
+ case 32000:
+ data = itv->card->gpio_audio_freq.f32000;
+ break;
+ case 44100:
+ data = itv->card->gpio_audio_freq.f44100;
+ break;
+ case 48000:
+ default:
+ data = itv->card->gpio_audio_freq.f48000;
break;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
- case VIDIOC_G_TUNER:
- mask = itv->card->gpio_audio_detect.mask;
- if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
- tuner->rxsubchans = V4L2_TUNER_MODE_STEREO |
- V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
- else
- tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
- return 0;
+static int subdev_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask;
+
+ mask = itv->card->gpio_audio_detect.mask;
+ if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
+ vt->rxsubchans = V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ else
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ return 0;
+}
- case VIDIOC_S_TUNER:
- mask = itv->card->gpio_audio_mode.mask;
- switch (tuner->audmode) {
- case V4L2_TUNER_MODE_LANG1:
- data = itv->card->gpio_audio_mode.lang1;
- break;
- case V4L2_TUNER_MODE_LANG2:
- data = itv->card->gpio_audio_mode.lang2;
- break;
- case V4L2_TUNER_MODE_MONO:
- data = itv->card->gpio_audio_mode.mono;
- break;
- case V4L2_TUNER_MODE_STEREO:
- case V4L2_TUNER_MODE_LANG1_LANG2:
- default:
- data = itv->card->gpio_audio_mode.stereo;
- break;
- }
- break;
+static int subdev_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
- case AUDC_SET_RADIO:
- mask = itv->card->gpio_audio_input.mask;
- data = itv->card->gpio_audio_input.radio;
+ mask = itv->card->gpio_audio_mode.mask;
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_LANG1:
+ data = itv->card->gpio_audio_mode.lang1;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ data = itv->card->gpio_audio_mode.lang2;
+ break;
+ case V4L2_TUNER_MODE_MONO:
+ data = itv->card->gpio_audio_mode.mono;
break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ default:
+ data = itv->card->gpio_audio_mode.stereo;
+ break;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_s_radio(struct v4l2_subdev *sd)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ mask = itv->card->gpio_audio_input.mask;
+ data = itv->card->gpio_audio_input.radio;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
- case VIDIOC_S_STD:
- mask = itv->card->gpio_audio_input.mask;
+static int subdev_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ mask = itv->card->gpio_audio_input.mask;
+ data = itv->card->gpio_audio_input.tuner;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ if (route->input > 2)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_input.mask;
+ switch (route->input) {
+ case 0:
data = itv->card->gpio_audio_input.tuner;
break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- if (route->input > 2)
- return -EINVAL;
- mask = itv->card->gpio_audio_input.mask;
- switch (route->input) {
- case 0:
- data = itv->card->gpio_audio_input.tuner;
- break;
- case 1:
- data = itv->card->gpio_audio_input.linein;
- break;
- case 2:
- default:
- data = itv->card->gpio_audio_input.radio;
- break;
- }
+ case 1:
+ data = itv->card->gpio_audio_input.linein;
+ break;
+ case 2:
+ default:
+ data = itv->card->gpio_audio_input.radio;
break;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
- case VIDIOC_G_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- mask = itv->card->gpio_audio_mute.mask;
- data = itv->card->gpio_audio_mute.mute;
- ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
- return 0;
+static int subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
- case VIDIOC_S_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- mask = itv->card->gpio_audio_mute.mask;
- data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
- break;
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_mute.mask;
+ data = itv->card->gpio_audio_mute.mute;
+ ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
+ return 0;
+}
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
+static int subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
- if (qc->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- *qc = gpio_ctrl_mute;
- return 0;
- }
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_mute.mask;
+ data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ if (qc->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ *qc = gpio_ctrl_mute;
+ return 0;
+}
+
+static int subdev_log_status(struct v4l2_subdev *sd)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
- case VIDIOC_LOG_STATUS:
- IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
+ IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
read_reg(IVTV_REG_GPIO_IN));
- return 0;
+ return 0;
+}
- case VIDIOC_INT_S_VIDEO_ROUTING:
- if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */
- return -EINVAL;
- mask = itv->card->gpio_video_input.mask;
- if (route->input == 0)
- data = itv->card->gpio_video_input.tuner;
- else if (route->input == 1)
- data = itv->card->gpio_video_input.composite;
- else
- data = itv->card->gpio_video_input.svideo;
- break;
+static int subdev_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
- default:
+ if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */
return -EINVAL;
- }
+ mask = itv->card->gpio_video_input.mask;
+ if (route->input == 0)
+ data = itv->card->gpio_video_input.tuner;
+ else if (route->input == 1)
+ data = itv->card->gpio_video_input.composite;
+ else
+ data = itv->card->gpio_video_input.svideo;
if (mask)
write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
return 0;
}
+
+static const struct v4l2_subdev_core_ops subdev_core_ops = {
+ .log_status = subdev_log_status,
+ .g_ctrl = subdev_g_ctrl,
+ .s_ctrl = subdev_s_ctrl,
+ .queryctrl = subdev_queryctrl,
+};
+
+static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = {
+ .s_std = subdev_s_std,
+ .s_radio = subdev_s_radio,
+ .g_tuner = subdev_g_tuner,
+ .s_tuner = subdev_s_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops subdev_audio_ops = {
+ .s_clock_freq = subdev_s_clock_freq,
+ .s_routing = subdev_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops subdev_video_ops = {
+ .s_routing = subdev_s_video_routing,
+};
+
+static const struct v4l2_subdev_ops subdev_ops = {
+ .core = &subdev_core_ops,
+ .tuner = &subdev_tuner_ops,
+ .audio = &subdev_audio_ops,
+ .video = &subdev_video_ops,
+};
+
+int ivtv_gpio_init(struct ivtv *itv)
+{
+ u16 pin = 0;
+
+ if (itv->card->xceive_pin)
+ pin = 1 << itv->card->xceive_pin;
+
+ if ((itv->card->gpio_init.direction | pin) == 0)
+ return 0;
+
+ IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
+ read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
+
+ /* init output data then direction */
+ write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT);
+ write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR);
+ v4l2_subdev_init(&itv->sd_gpio, &subdev_ops);
+ snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->device.name);
+ itv->sd_gpio.grp_id = IVTV_HW_GPIO;
+ return v4l2_device_register_subdev(&itv->device, &itv->sd_gpio);
+}
diff --git a/drivers/media/video/ivtv/ivtv-gpio.h b/drivers/media/video/ivtv/ivtv-gpio.h
index 48b6291613a2..0b5d19c8ecb4 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.h
+++ b/drivers/media/video/ivtv/ivtv-gpio.h
@@ -22,9 +22,8 @@
#define IVTV_GPIO_H
/* GPIO stuff */
-void ivtv_gpio_init(struct ivtv *itv);
+int ivtv_gpio_init(struct ivtv *itv);
void ivtv_reset_ir_gpio(struct ivtv *itv);
int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value);
-int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg);
#endif
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
index 41dbbe9621a1..ca1d9557945e 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.c
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -90,26 +90,6 @@
#define IVTV_M52790_I2C_ADDR 0x48
/* This array should match the IVTV_HW_ defines */
-static const u8 hw_driverids[] = {
- I2C_DRIVERID_CX25840,
- I2C_DRIVERID_SAA711X,
- I2C_DRIVERID_SAA7127,
- I2C_DRIVERID_MSP3400,
- I2C_DRIVERID_TUNER,
- I2C_DRIVERID_WM8775,
- I2C_DRIVERID_CS53L32A,
- I2C_DRIVERID_TVEEPROM,
- I2C_DRIVERID_SAA711X,
- I2C_DRIVERID_UPD64031A,
- I2C_DRIVERID_UPD64083,
- I2C_DRIVERID_SAA717X,
- I2C_DRIVERID_WM8739,
- I2C_DRIVERID_VP27SMPX,
- I2C_DRIVERID_M52790,
- 0 /* IVTV_HW_GPIO dummy driver ID */
-};
-
-/* This array should match the IVTV_HW_ defines */
static const u8 hw_addrs[] = {
IVTV_CX25840_I2C_ADDR,
IVTV_SAA7115_I2C_ADDR,
@@ -130,6 +110,26 @@ static const u8 hw_addrs[] = {
};
/* This array should match the IVTV_HW_ defines */
+static const char *hw_modules[] = {
+ "cx25840",
+ "saa7115",
+ "saa7127",
+ "msp3400",
+ "tuner",
+ "wm8775",
+ "cs53l32a",
+ NULL,
+ "saa7115",
+ "upd64031a",
+ "upd64083",
+ "saa717x",
+ "wm8739",
+ "vp27smpx",
+ "m52790",
+ NULL
+};
+
+/* This array should match the IVTV_HW_ defines */
static const char * const hw_devicenames[] = {
"cx25840",
"saa7115",
@@ -151,80 +151,58 @@ static const char * const hw_devicenames[] = {
int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
{
- struct i2c_board_info info;
- struct i2c_client *c;
- u8 id;
- int i;
+ struct v4l2_subdev *sd;
+ struct i2c_adapter *adap = &itv->i2c_adap;
+ const char *mod = hw_modules[idx];
+ const char *type = hw_devicenames[idx];
+ u32 hw = 1 << idx;
- IVTV_DEBUG_I2C("i2c client register\n");
- if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
+ if (idx >= ARRAY_SIZE(hw_addrs))
return -1;
- id = hw_driverids[idx];
- memset(&info, 0, sizeof(info));
- strlcpy(info.type, hw_devicenames[idx], sizeof(info.type));
- info.addr = hw_addrs[idx];
- for (i = 0; itv->i2c_clients[i] && i < I2C_CLIENTS_MAX; i++) {}
-
- if (i == I2C_CLIENTS_MAX) {
- IVTV_ERR("insufficient room for new I2C client!\n");
- return -ENOMEM;
+ if (hw == IVTV_HW_TUNER) {
+ /* special tuner handling */
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+ itv->card_i2c->radio);
+ if (sd)
+ sd->grp_id = 1 << idx;
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+ itv->card_i2c->demod);
+ if (sd)
+ sd->grp_id = 1 << idx;
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+ itv->card_i2c->tv);
+ if (sd)
+ sd->grp_id = 1 << idx;
+ return sd ? 0 : -1;
}
+ if (!hw_addrs[idx])
+ return -1;
+ if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
+ unsigned short addrs[2] = { hw_addrs[idx], I2C_CLIENT_END };
- if (id != I2C_DRIVERID_TUNER) {
- if (id == I2C_DRIVERID_UPD64031A ||
- id == I2C_DRIVERID_UPD64083) {
- unsigned short addrs[2] = { info.addr, I2C_CLIENT_END };
-
- c = i2c_new_probed_device(&itv->i2c_adap, &info, addrs);
- } else
- c = i2c_new_device(&itv->i2c_adap, &info);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- itv->i2c_clients[i] = c;
- return itv->i2c_clients[i] ? 0 : -ENODEV;
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type, addrs);
+ } else {
+ sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]);
}
-
- /* special tuner handling */
- c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->radio);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- itv->i2c_clients[i++] = c;
- c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->demod);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- itv->i2c_clients[i++] = c;
- c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->tv);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- itv->i2c_clients[i++] = c;
- return 0;
-}
-
-static int attach_inform(struct i2c_client *client)
-{
- return 0;
+ if (sd)
+ sd->grp_id = 1 << idx;
+ return sd ? 0 : -1;
}
-static int detach_inform(struct i2c_client *client)
+struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw)
{
- int i;
- struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
+ struct v4l2_subdev *result = NULL;
+ struct v4l2_subdev *sd;
- IVTV_DEBUG_I2C("i2c client detach\n");
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- if (itv->i2c_clients[i] == client) {
- itv->i2c_clients[i] = NULL;
+ spin_lock(&itv->device.lock);
+ v4l2_device_for_each_subdev(sd, &itv->device) {
+ if (sd->grp_id == hw) {
+ result = sd;
break;
}
}
- IVTV_DEBUG_I2C("i2c detach [client=%s,%s]\n",
- client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
-
- return 0;
+ spin_unlock(&itv->device.lock);
+ return result;
}
/* Set the serial clock line to the desired state */
@@ -494,7 +472,8 @@ static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data,
intervening stop condition */
static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
{
- struct ivtv *itv = i2c_get_adapdata(i2c_adap);
+ struct v4l2_device *drv = i2c_get_adapdata(i2c_adap);
+ struct ivtv *itv = to_ivtv(drv);
int retval;
int i;
@@ -530,8 +509,6 @@ static struct i2c_adapter ivtv_i2c_adap_hw_template = {
.id = I2C_HW_B_CX2341X,
.algo = &ivtv_algo,
.algo_data = NULL, /* filled from template */
- .client_register = attach_inform,
- .client_unregister = detach_inform,
.owner = THIS_MODULE,
};
@@ -583,8 +560,6 @@ static struct i2c_adapter ivtv_i2c_adap_template = {
.id = I2C_HW_B_CX2341X,
.algo = NULL, /* set by i2c-algo-bit */
.algo_data = NULL, /* filled from template */
- .client_register = attach_inform,
- .client_unregister = detach_inform,
.owner = THIS_MODULE,
};
@@ -601,160 +576,6 @@ static struct i2c_client ivtv_i2c_client_template = {
.name = "ivtv internal",
};
-int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg)
-{
- struct i2c_client *client;
- int retval;
- int i;
-
- IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- client = itv->i2c_clients[i];
- if (client == NULL || client->driver == NULL ||
- client->driver->command == NULL)
- continue;
- if (addr == client->addr) {
- retval = client->driver->command(client, cmd, arg);
- return retval;
- }
- }
- if (cmd != VIDIOC_G_CHIP_IDENT)
- IVTV_ERR("i2c addr 0x%02x not found for command 0x%x\n", addr, cmd);
- return -ENODEV;
-}
-
-/* Find the i2c device based on the driver ID and return
- its i2c address or -ENODEV if no matching device was found. */
-static int ivtv_i2c_id_addr(struct ivtv *itv, u32 id)
-{
- struct i2c_client *client;
- int retval = -ENODEV;
- int i;
-
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- client = itv->i2c_clients[i];
- if (client == NULL || client->driver == NULL)
- continue;
- if (id == client->driver->id) {
- retval = client->addr;
- break;
- }
- }
- return retval;
-}
-
-/* Find the i2c device name matching the DRIVERID */
-static const char *ivtv_i2c_id_name(u32 id)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
- if (hw_driverids[i] == id)
- return hw_devicenames[i];
- return "unknown device";
-}
-
-/* Find the i2c device name matching the IVTV_HW_ flag */
-static const char *ivtv_i2c_hw_name(u32 hw)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
- if (1 << i == hw)
- return hw_devicenames[i];
- return "unknown device";
-}
-
-/* Find the i2c device matching the IVTV_HW_ flag and return
- its i2c address or -ENODEV if no matching device was found. */
-int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
- if (1 << i == hw)
- return ivtv_i2c_id_addr(itv, hw_driverids[i]);
- return -ENODEV;
-}
-
-/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing.
- If hw == IVTV_HW_GPIO then call the gpio handler. */
-int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg)
-{
- int addr;
-
- if (hw == IVTV_HW_GPIO)
- return ivtv_gpio(itv, cmd, arg);
- if (hw == 0)
- return 0;
-
- addr = ivtv_i2c_hw_addr(itv, hw);
- if (addr < 0) {
- IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x\n",
- hw, ivtv_i2c_hw_name(hw), cmd);
- return addr;
- }
- return ivtv_call_i2c_client(itv, addr, cmd, arg);
-}
-
-/* Calls i2c device based on I2C driver ID. */
-int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg)
-{
- int addr;
-
- addr = ivtv_i2c_id_addr(itv, id);
- if (addr < 0) {
- if (cmd != VIDIOC_G_CHIP_IDENT)
- IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x\n",
- id, ivtv_i2c_id_name(id), cmd);
- return addr;
- }
- return ivtv_call_i2c_client(itv, addr, cmd, arg);
-}
-
-int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg);
-}
-
-int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg);
-}
-
-int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg);
-}
-EXPORT_SYMBOL(ivtv_saa7127);
-
-int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg);
-}
-
-int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg);
-}
-
-int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg);
-}
-
-/* broadcast cmd for all I2C clients and for the gpio subsystem */
-void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- if (itv->i2c_adap.algo == NULL) {
- IVTV_ERR("Adapter is not set");
- return;
- }
- i2c_clients_command(&itv->i2c_adap, cmd, arg);
- if (itv->hw_flags & IVTV_HW_GPIO)
- ivtv_gpio(itv, cmd, arg);
-}
-
/* init + register i2c algo-bit adapter */
int init_ivtv_i2c(struct ivtv *itv)
{
@@ -763,10 +584,9 @@ int init_ivtv_i2c(struct ivtv *itv)
/* Sanity checks for the I2C hardware arrays. They must be the
* same size and GPIO must be the last entry.
*/
- if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) ||
- ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
- IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1)) ||
- hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) {
+ if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
+ ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules) ||
+ IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1))) {
IVTV_ERR("Mismatched I2C hardware arrays\n");
return -ENODEV;
}
@@ -783,8 +603,8 @@ int init_ivtv_i2c(struct ivtv *itv)
itv->i2c_adap.algo_data = &itv->i2c_algo;
sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d",
- itv->num);
- i2c_set_adapdata(&itv->i2c_adap, itv);
+ itv->instance);
+ i2c_set_adapdata(&itv->i2c_adap, &itv->device);
memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
sizeof(struct i2c_client));
diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h
index 022978cf533d..396928a06a54 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.h
+++ b/drivers/media/video/ivtv/ivtv-i2c.h
@@ -21,19 +21,8 @@
#ifndef IVTV_I2C_H
#define IVTV_I2C_H
-int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg);
-
-int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw);
-int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg);
-int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg);
-int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg);
-void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg);
int ivtv_i2c_register(struct ivtv *itv, unsigned idx);
+struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw);
/* init + register i2c algo-bit adapter */
int init_ivtv_i2c(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 4bae38d21ef6..cd990a4b81a9 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -393,7 +393,7 @@ static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo
return 0;
}
- itv->video_dec_func(itv, VIDIOC_G_FMT, fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
vbifmt->service_set = ivtv_get_service_set(vbifmt);
return 0;
}
@@ -581,7 +581,7 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
p->height = h;
if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
fmt->fmt.pix.width /= 2;
- itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
return ivtv_g_fmt_vid_cap(file, fh, fmt);
}
@@ -593,7 +593,7 @@ static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f
return -EBUSY;
itv->vbi.sliced_in->service_set = 0;
itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
- itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
return ivtv_g_fmt_vbi_cap(file, fh, fmt);
}
@@ -611,7 +611,7 @@ static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo
if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
return -EBUSY;
itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
- itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
return 0;
}
@@ -685,18 +685,17 @@ static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_chip_ident
chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
return 0;
}
- if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
- return ivtv_i2c_id(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip);
- if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
- return ivtv_call_i2c_client(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip);
- return -EINVAL;
+ if (chip->match_type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match_type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+ /* TODO: is this correct? */
+ return ivtv_call_all_err(itv, core, g_chip_ident, chip);
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
{
struct v4l2_register *regs = arg;
- unsigned long flags;
volatile u8 __iomem *reg_start;
if (!capable(CAP_SYS_ADMIN))
@@ -711,12 +710,10 @@ static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
else
return -EINVAL;
- spin_lock_irqsave(&ivtv_cards_lock, flags);
if (cmd == VIDIOC_DBG_G_REGISTER)
regs->val = readl(regs->reg + reg_start);
else
writel(regs->val, regs->reg + reg_start);
- spin_unlock_irqrestore(&ivtv_cards_lock, flags);
return 0;
}
@@ -726,9 +723,10 @@ static int ivtv_g_register(struct file *file, void *fh, struct v4l2_register *re
if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg);
- if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
- return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg);
- return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg);
+ /* TODO: subdev errors should not be ignored, this should become a
+ subdev helper function. */
+ ivtv_call_all(itv, core, g_register, reg);
+ return 0;
}
static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *reg)
@@ -737,9 +735,10 @@ static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *re
if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg);
- if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
- return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg);
- return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg);
+ /* TODO: subdev errors should not be ignored, this should become a
+ subdev helper function. */
+ ivtv_call_all(itv, core, s_register, reg);
+ return 0;
}
#endif
@@ -884,12 +883,6 @@ static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
streamtype = id->type;
- if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
- printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
- /* Should be replaced */
- /* v4l_printk_ioctl(VIDIOC_S_CROP); */
- }
-
if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
@@ -1050,7 +1043,7 @@ static int ivtv_s_output(struct file *file, void *fh, unsigned int outp)
itv->active_output = outp;
route.input = SAA7127_INPUT_TYPE_NORMAL;
route.output = itv->card->video_outputs[outp].video_output;
- ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, &route);
return 0;
}
@@ -1062,7 +1055,7 @@ static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency *
if (vf->tuner != 0)
return -EINVAL;
- ivtv_call_i2c_clients(itv, VIDIOC_G_FREQUENCY, vf);
+ ivtv_call_all(itv, tuner, g_frequency, vf);
return 0;
}
@@ -1075,7 +1068,7 @@ int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
ivtv_mute(itv);
IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
- ivtv_call_i2c_clients(itv, VIDIOC_S_FREQUENCY, vf);
+ ivtv_call_all(itv, tuner, s_frequency, vf);
ivtv_unmute(itv);
return 0;
}
@@ -1123,14 +1116,14 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
IVTV_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)itv->std);
/* Tuner */
- ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+ ivtv_call_all(itv, tuner, s_std, itv->std);
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
/* set display standard */
itv->std_out = *std;
itv->is_out_60hz = itv->is_60hz;
itv->is_out_50hz = itv->is_50hz;
- ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out);
+ ivtv_call_all(itv, video, s_std_output, itv->std_out);
ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
itv->main_rect.left = itv->main_rect.top = 0;
itv->main_rect.width = 720;
@@ -1154,7 +1147,7 @@ static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
if (vt->index != 0)
return -EINVAL;
- ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt);
+ ivtv_call_all(itv, tuner, s_tuner, vt);
return 0;
}
@@ -1166,7 +1159,7 @@ static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
if (vt->index != 0)
return -EINVAL;
- ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt);
+ ivtv_call_all(itv, tuner, g_tuner, vt);
if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name));
@@ -1444,14 +1437,15 @@ static int ivtv_log_status(struct file *file, void *fh)
struct v4l2_audio audin;
int i;
- IVTV_INFO("================= START STATUS CARD #%d =================\n", itv->num);
+ IVTV_INFO("================= START STATUS CARD #%d =================\n",
+ itv->instance);
IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name);
if (itv->hw_flags & IVTV_HW_TVEEPROM) {
struct tveeprom tv;
ivtv_read_eeprom(itv, &tv);
}
- ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL);
+ ivtv_call_all(itv, core, log_status);
ivtv_get_input(itv, itv->active_input, &vidin);
ivtv_get_audio_input(itv, itv->audio_input, &audin);
IVTV_INFO("Video Input: %s\n", vidin.name);
@@ -1518,7 +1512,7 @@ static int ivtv_log_status(struct file *file, void *fh)
}
IVTV_INFO("Tuner: %s\n",
test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
- cx2341x_log_status(&itv->params, itv->name);
+ cx2341x_log_status(&itv->params, itv->device.name);
IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
for (i = 0; i < IVTV_MAX_STREAMS; i++) {
struct ivtv_stream *s = &itv->streams[i];
@@ -1530,8 +1524,11 @@ static int ivtv_log_status(struct file *file, void *fh)
(s->buffers * s->buf_size) / 1024, s->buffers);
}
- IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", (long long)itv->mpg_data_received, (long long)itv->vbi_data_inserted);
- IVTV_INFO("================== END STATUS CARD #%d ==================\n", itv->num);
+ IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n",
+ (long long)itv->mpg_data_received,
+ (long long)itv->vbi_data_inserted);
+ IVTV_INFO("================== END STATUS CARD #%d ==================\n",
+ itv->instance);
return 0;
}
@@ -1736,7 +1733,7 @@ static int ivtv_default(struct file *file, void *fh, int cmd, void *arg)
case VIDIOC_INT_S_AUDIO_ROUTING: {
struct v4l2_routing *route = arg;
- ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route);
+ ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, route);
break;
}
@@ -1746,7 +1743,7 @@ static int ivtv_default(struct file *file, void *fh, int cmd, void *arg)
if ((val == 0 && itv->options.newi2c) || (val & 0x01))
ivtv_reset_ir_gpio(itv);
if (val & 0x02)
- itv->video_dec_func(itv, cmd, NULL);
+ v4l2_subdev_call(itv->sd_video, core, reset, 0);
break;
}
diff --git a/drivers/media/video/ivtv/ivtv-routing.c b/drivers/media/video/ivtv/ivtv-routing.c
index 05564919b57f..3fd302294497 100644
--- a/drivers/media/video/ivtv/ivtv-routing.c
+++ b/drivers/media/video/ivtv/ivtv-routing.c
@@ -47,13 +47,13 @@ void ivtv_audio_set_io(struct ivtv *itv)
route.output = 0;
if (itv->card->hw_muxer & IVTV_HW_M52790)
route.output = M52790_OUT_STEREO;
- ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+ v4l2_subdev_call(itv->sd_muxer, audio, s_routing, &route);
route.input = in->audio_input;
route.output = 0;
if (itv->card->hw_audio & IVTV_HW_MSP34XX)
route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
- ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+ ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, &route);
}
/* Selects the video input and output according to the current
@@ -66,7 +66,7 @@ void ivtv_video_set_io(struct ivtv *itv)
route.input = itv->card->video_inputs[inp].video_input;
route.output = 0;
- itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ v4l2_subdev_call(itv->sd_video, video, s_routing, &route);
type = itv->card->video_inputs[inp].video_type;
@@ -79,7 +79,7 @@ void ivtv_video_set_io(struct ivtv *itv)
}
if (itv->card->hw_video & IVTV_HW_GPIO)
- ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ ivtv_call_hw(itv, IVTV_HW_GPIO, video, s_routing, &route);
if (itv->card->hw_video & IVTV_HW_UPD64031A) {
if (type == IVTV_CARD_INPUT_VID_TUNER ||
@@ -92,7 +92,7 @@ void ivtv_video_set_io(struct ivtv *itv)
}
route.input |= itv->card->gr_config;
- ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ ivtv_call_hw(itv, IVTV_HW_UPD64031A, video, s_routing, &route);
}
if (itv->card->hw_video & IVTV_HW_UPD6408X) {
@@ -110,6 +110,6 @@ void ivtv_video_set_io(struct ivtv *itv)
route.input |= UPD64083_EXT_Y_ADC;
}
}
- ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ ivtv_call_hw(itv, IVTV_HW_UPD6408X, video, s_routing, &route);
}
}
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index 9b7aa79eb267..f77d764707b2 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -172,7 +172,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
{
struct ivtv_stream *s = &itv->streams[type];
int num_offset = ivtv_stream_info[type].num_offset;
- int num = itv->num + ivtv_first_minor + num_offset;
+ int num = itv->instance + ivtv_first_minor + num_offset;
/* These four fields are always initialized. If v4l2dev == NULL, then
this stream is not in use. In that case no other fields but these
@@ -205,11 +205,11 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
return -ENOMEM;
}
- snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s",
- itv->num, s->name);
+ snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "%s %s",
+ itv->device.name, s->name);
s->v4l2dev->num = num;
- s->v4l2dev->parent = &itv->dev->dev;
+ s->v4l2dev->v4l2_dev = &itv->device;
s->v4l2dev->fops = ivtv_stream_info[type].fops;
s->v4l2dev->release = video_device_release;
s->v4l2dev->tvnorms = V4L2_STD_ALL;
@@ -260,6 +260,7 @@ static int ivtv_reg_dev(struct ivtv *itv, int type)
if (s_mpg->v4l2dev)
num = s_mpg->v4l2dev->num + ivtv_stream_info[type].num_offset;
}
+ video_set_drvdata(s->v4l2dev, s);
/* Register device. First try the desired minor, then any free one. */
if (video_register_device(s->v4l2dev, vfl_type, num)) {
@@ -343,7 +344,7 @@ static void ivtv_vbi_setup(struct ivtv *itv)
ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0);
/* setup VBI registers */
- itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, &itv->vbi.in);
/* determine number of lines and total number of VBI bytes.
A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
@@ -577,10 +578,10 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
clear_bit(IVTV_F_I_EOS, &itv->i_flags);
/* Initialize Digitizer for Capture */
- itv->video_dec_func(itv, VIDIOC_STREAMOFF, NULL);
+ v4l2_subdev_call(itv->sd_video, video, s_stream, 0);
ivtv_msleep_timeout(300, 1);
ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
- itv->video_dec_func(itv, VIDIOC_STREAMON, NULL);
+ v4l2_subdev_call(itv->sd_video, video, s_stream, 1);
}
/* begin_capture */
diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c
index 4a37a7d2e69d..5c5d1c462fef 100644
--- a/drivers/media/video/ivtv/ivtv-vbi.c
+++ b/drivers/media/video/ivtv/ivtv-vbi.c
@@ -21,6 +21,7 @@
#include "ivtv-i2c.h"
#include "ivtv-ioctl.h"
#include "ivtv-queue.h"
+#include "ivtv-cards.h"
#include "ivtv-vbi.h"
static void ivtv_set_vps(struct ivtv *itv, int enabled)
@@ -37,7 +38,7 @@ static void ivtv_set_vps(struct ivtv *itv, int enabled)
data.data[9] = itv->vbi.vps_payload.data[2];
data.data[10] = itv->vbi.vps_payload.data[3];
data.data[11] = itv->vbi.vps_payload.data[4];
- ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data);
}
static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc)
@@ -51,12 +52,12 @@ static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc)
data.line = (mode & 1) ? 21 : 0;
data.data[0] = cc->odd[0];
data.data[1] = cc->odd[1];
- ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data);
data.field = 1;
data.line = (mode & 2) ? 21 : 0;
data.data[0] = cc->even[0];
data.data[1] = cc->even[1];
- ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data);
}
static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode)
@@ -79,7 +80,7 @@ static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode)
data.line = enabled ? 23 : 0;
data.data[0] = mode & 0xff;
data.data[1] = (mode >> 8) & 0xff;
- ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data);
}
static int odd_parity(u8 c)
@@ -313,7 +314,7 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8
continue;
}
vbi.p = p + 4;
- itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi);
+ v4l2_subdev_call(itv->sd_video, video, decode_vbi_line, &vbi);
if (vbi.type && !(lines & (1 << vbi.line))) {
lines |= 1 << vbi.line;
itv->vbi.sliced_data[line].id = vbi.type;
@@ -437,7 +438,7 @@ void ivtv_vbi_work_handler(struct ivtv *itv)
data.id = V4L2_SLICED_WSS_625;
data.field = 0;
- if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+ if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) {
ivtv_set_wss(itv, 1, data.data[0] & 0xf);
vi->wss_missing_cnt = 0;
} else if (vi->wss_missing_cnt == 4) {
@@ -451,13 +452,13 @@ void ivtv_vbi_work_handler(struct ivtv *itv)
data.id = V4L2_SLICED_CAPTION_525;
data.field = 0;
- if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+ if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) {
mode |= 1;
cc.odd[0] = data.data[0];
cc.odd[1] = data.data[1];
}
data.field = 1;
- if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+ if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) {
mode |= 2;
cc.even[0] = data.data[0];
cc.even[1] = data.data[1];
diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c
index 921e281876f8..36abd2aef6f1 100644
--- a/drivers/media/video/ivtv/ivtvfb.c
+++ b/drivers/media/video/ivtv/ivtvfb.c
@@ -48,6 +48,7 @@
#endif
#include "ivtv-driver.h"
+#include "ivtv-cards.h"
#include "ivtv-i2c.h"
#include "ivtv-udma.h"
#include "ivtv-mailbox.h"
@@ -121,15 +122,15 @@ MODULE_LICENSE("GPL");
#define IVTVFB_DEBUG(x, type, fmt, args...) \
do { \
if ((x) & ivtvfb_debug) \
- printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->num , ## args); \
+ printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \
} while (0)
#define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args)
#define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args)
/* Standard kernel messages */
-#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->num , ## args)
-#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->num , ## args)
-#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->num , ## args)
+#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->instance , ## args)
+#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->instance , ## args)
+#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args)
/* --------------------------------------------------------------------- */
@@ -895,16 +896,16 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info)
switch (blank_mode) {
case FB_BLANK_UNBLANK:
ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
- ivtv_saa7127(itv, VIDIOC_STREAMON, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
break;
case FB_BLANK_NORMAL:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_VSYNC_SUSPEND:
ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
- ivtv_saa7127(itv, VIDIOC_STREAMON, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
break;
case FB_BLANK_POWERDOWN:
- ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
break;
}
@@ -1188,10 +1189,45 @@ static int ivtvfb_init_card(struct ivtv *itv)
}
+static int __init ivtvfb_callback_init(struct device *dev, void *p)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+ struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device);
+
+ if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+ if (ivtvfb_init_card(itv) == 0) {
+ IVTVFB_INFO("Framebuffer registered on %s\n",
+ itv->device.name);
+ (*(int *)p)++;
+ }
+ }
+ return 0;
+}
+
+static int ivtvfb_callback_cleanup(struct device *dev, void *p)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+ struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device);
+
+ if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+ if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
+ IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n",
+ itv->instance);
+ return 0;
+ }
+ IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance);
+ ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
+ ivtvfb_release_buffers(itv);
+ itv->osd_video_pbase = 0;
+ }
+ return 0;
+}
+
static int __init ivtvfb_init(void)
{
- struct ivtv *itv;
- int i, registered = 0;
+ struct device_driver *drv;
+ int registered = 0;
+ int err;
if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
@@ -1199,20 +1235,11 @@ static int __init ivtvfb_init(void)
return -EINVAL;
}
- /* Locate & initialise all cards supporting an OSD. */
- for (i = 0; i < ivtv_cards_active; i++) {
- if (ivtvfb_card_id != -1 && i != ivtvfb_card_id)
- continue;
- itv = ivtv_cards[i];
- if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
- if (ivtvfb_init_card(itv) == 0) {
- IVTVFB_INFO("Framebuffer registered on ivtv card id %d\n", i);
- registered++;
- }
- }
- }
+ drv = driver_find("ivtv", &pci_bus_type);
+ err = driver_for_each_device(drv, NULL, &registered, ivtvfb_callback_init);
+ put_driver(drv);
if (!registered) {
- printk(KERN_ERR "ivtvfb: no cards found");
+ printk(KERN_ERR "ivtvfb: no cards found\n");
return -ENODEV;
}
return 0;
@@ -1220,24 +1247,14 @@ static int __init ivtvfb_init(void)
static void ivtvfb_cleanup(void)
{
- struct ivtv *itv;
- int i;
+ struct device_driver *drv;
+ int err;
printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n");
- for (i = 0; i < ivtv_cards_active; i++) {
- itv = ivtv_cards[i];
- if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
- if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
- IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i);
- return;
- }
- IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i);
- ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
- ivtvfb_release_buffers(itv);
- itv->osd_video_pbase = 0;
- }
- }
+ drv = driver_find("ivtv", &pci_bus_type);
+ err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup);
+ put_driver(drv);
}
module_init(ivtvfb_init);
diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c
index 89a781c6929d..07be14a9fe7b 100644
--- a/drivers/media/video/m52790.c
+++ b/drivers/media/video/m52790.c
@@ -28,7 +28,7 @@
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
#include <media/m52790.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
@@ -38,89 +38,130 @@ MODULE_LICENSE("GPL");
struct m52790_state {
+ struct v4l2_subdev sd;
u16 input;
u16 output;
};
+static inline struct m52790_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct m52790_state, sd);
+}
+
/* ----------------------------------------------------------------------- */
-static int m52790_write(struct i2c_client *client)
+static int m52790_write(struct v4l2_subdev *sd)
{
- struct m52790_state *state = i2c_get_clientdata(client);
+ struct m52790_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
u8 sw1 = (state->input | state->output) & 0xff;
u8 sw2 = (state->input | state->output) >> 8;
return i2c_smbus_write_byte_data(client, sw1, sw2);
}
-static int m52790_command(struct i2c_client *client, unsigned int cmd,
- void *arg)
+/* Note: audio and video are linked and cannot be switched separately.
+ So audio and video routing commands are identical for this chip.
+ In theory the video amplifier and audio modes could be handled
+ separately for the output, but that seems to be overkill right now.
+ The same holds for implementing an audio mute control, this is now
+ part of the audio output routing. The normal case is that another
+ chip takes care of the actual muting so making it part of the
+ output routing seems to be the right thing to do for now. */
+static int m52790_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct m52790_state *state = i2c_get_clientdata(client);
- struct v4l2_routing *route = arg;
-
- /* Note: audio and video are linked and cannot be switched separately.
- So audio and video routing commands are identical for this chip.
- In theory the video amplifier and audio modes could be handled
- separately for the output, but that seems to be overkill right now.
- The same holds for implementing an audio mute control, this is now
- part of the audio output routing. The normal case is that another
- chip takes care of the actual muting so making it part of the
- output routing seems to be the right thing to do for now. */
- switch (cmd) {
- case VIDIOC_INT_G_AUDIO_ROUTING:
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = state->input;
- route->output = state->output;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- case VIDIOC_INT_S_VIDEO_ROUTING:
- state->input = route->input;
- state->output = route->output;
- m52790_write(client);
- break;
+ struct m52790_state *state = to_state(sd);
+
+ state->input = route->input;
+ state->output = route->output;
+ m52790_write(sd);
+ return 0;
+}
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client,
- reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (reg->reg != 0)
- return -EINVAL;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = state->input | state->output;
- else {
- state->input = reg->val & 0x0303;
- state->output = reg->val & ~0x0303;
- m52790_write(client);
- }
- break;
- }
-#endif
+static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct m52790_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg,
- V4L2_IDENT_M52790, 0);
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (reg->reg != 0)
+ return -EINVAL;
+ reg->val = state->input | state->output;
+ return 0;
+}
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Switch 1: %02x\n",
- (state->input | state->output) & 0xff);
- v4l_info(client, "Switch 2: %02x\n",
- (state->input | state->output) >> 8);
- break;
+static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct m52790_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- default:
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
return -EINVAL;
- }
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (reg->reg != 0)
+ return -EINVAL;
+ state->input = reg->val & 0x0303;
+ state->output = reg->val & ~0x0303;
+ m52790_write(sd);
return 0;
}
+#endif
+
+static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0);
+}
+
+static int m52790_log_status(struct v4l2_subdev *sd)
+{
+ struct m52790_state *state = to_state(sd);
+
+ v4l2_info(sd, "Switch 1: %02x\n",
+ (state->input | state->output) & 0xff);
+ v4l2_info(sd, "Switch 2: %02x\n",
+ (state->input | state->output) >> 8);
+ return 0;
+}
+
+static int m52790_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops m52790_core_ops = {
+ .log_status = m52790_log_status,
+ .g_chip_ident = m52790_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = m52790_g_register,
+ .s_register = m52790_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_audio_ops m52790_audio_ops = {
+ .s_routing = m52790_s_routing,
+};
+
+static const struct v4l2_subdev_video_ops m52790_video_ops = {
+ .s_routing = m52790_s_routing,
+};
+
+static const struct v4l2_subdev_ops m52790_ops = {
+ .core = &m52790_core_ops,
+ .audio = &m52790_audio_ops,
+ .video = &m52790_video_ops,
+};
/* ----------------------------------------------------------------------- */
@@ -130,6 +171,7 @@ static int m52790_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct m52790_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -142,16 +184,20 @@ static int m52790_probe(struct i2c_client *client,
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &m52790_ops);
state->input = M52790_IN_TUNER;
state->output = M52790_OUT_STEREO;
- i2c_set_clientdata(client, state);
- m52790_write(client);
+ m52790_write(sd);
return 0;
}
static int m52790_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
index 3da74dcee902..a622dbb72ed8 100644
--- a/drivers/media/video/msp3400-driver.c
+++ b/drivers/media/video/msp3400-driver.c
@@ -51,14 +51,14 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-i2c-drv-legacy.h>
-#include <media/tvaudio.h>
#include <media/msp3400.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
+#include <media/tvaudio.h>
#include "msp3400-driver.h"
/* ---------------------------------------------------------------------- */
@@ -265,7 +265,7 @@ static char *scart_names[] = {
void msp_set_scart(struct i2c_client *client, int in, int out)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
state->in_scart = in;
@@ -289,7 +289,7 @@ void msp_set_scart(struct i2c_client *client, int in, int out)
void msp_set_audio(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int bal = 0, bass, treble, loudness;
int val = 0;
int reallymuted = state->muted | state->scan_in_progress;
@@ -336,7 +336,7 @@ void msp_set_audio(struct i2c_client *client)
static void msp_wake_thread(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (NULL == state->kthread)
return;
@@ -390,9 +390,9 @@ static int msp_mode_v4l1_to_v4l2(int mode)
}
#endif
-static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
@@ -433,9 +433,10 @@ static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
return 0;
}
-static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
@@ -481,40 +482,16 @@ static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
return 0;
}
-static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
+#ifdef CONFIG_VIDEO_ALLOW_V4L1
+static int msp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
- struct msp_state *state = i2c_get_clientdata(client);
-
- if (msp_debug >= 2)
- v4l_i2c_print_ioctl(client, cmd);
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (cmd) {
- case AUDC_SET_RADIO:
- if (state->radio)
- return 0;
- state->radio = 1;
- v4l_dbg(1, msp_debug, client, "switching to radio mode\n");
- state->watch_stereo = 0;
- switch (state->opmode) {
- case OPMODE_MANUAL:
- /* set msp3400 to FM radio mode */
- msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
- msp3400c_set_carrier(client, MSP_CARRIER(10.7),
- MSP_CARRIER(10.7));
- msp_set_audio(client);
- break;
- case OPMODE_AUTODETECT:
- case OPMODE_AUTOSELECT:
- /* the thread will do for us */
- msp_wake_thread(client);
- break;
- }
- break;
-
/* --- v4l ioctls --- */
/* take care: bttv does userspace copying, we'll get a
kernel pointer here... */
-#ifdef CONFIG_VIDEO_ALLOW_V4L1
case VIDIOCGAUDIO:
{
struct video_audio *va = arg;
@@ -588,105 +565,137 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
msp_wake_thread(client);
break;
}
-#endif
- case VIDIOC_S_FREQUENCY:
- {
- /* new channel -- kick audio carrier scan */
- msp_wake_thread(client);
- break;
+ default:
+ return -ENOIOCTLCMD;
}
+ return 0;
+}
+#endif
- /* --- v4l2 ioctls --- */
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg;
- int update = state->radio || state->v4l2_std != *id;
+/* --- v4l2 ioctls --- */
+static int msp_s_radio(struct v4l2_subdev *sd)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- state->v4l2_std = *id;
- state->radio = 0;
- if (update)
- msp_wake_thread(client);
+ if (state->radio)
return 0;
+ state->radio = 1;
+ v4l_dbg(1, msp_debug, client, "switching to radio mode\n");
+ state->watch_stereo = 0;
+ switch (state->opmode) {
+ case OPMODE_MANUAL:
+ /* set msp3400 to FM radio mode */
+ msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
+ msp3400c_set_carrier(client, MSP_CARRIER(10.7),
+ MSP_CARRIER(10.7));
+ msp_set_audio(client);
+ break;
+ case OPMODE_AUTODETECT:
+ case OPMODE_AUTOSELECT:
+ /* the thread will do for us */
+ msp_wake_thread(client);
+ break;
}
+ return 0;
+}
- case VIDIOC_INT_G_AUDIO_ROUTING:
- {
- struct v4l2_routing *rt = arg;
+static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- *rt = state->routing;
- break;
- }
+ /* new channel -- kick audio carrier scan */
+ msp_wake_thread(client);
+ return 0;
+}
- case VIDIOC_INT_S_AUDIO_ROUTING:
- {
- struct v4l2_routing *rt = arg;
- int tuner = (rt->input >> 3) & 1;
- int sc_in = rt->input & 0x7;
- int sc1_out = rt->output & 0xf;
- int sc2_out = (rt->output >> 4) & 0xf;
- u16 val, reg;
- int i;
- int extern_input = 1;
-
- if (state->routing.input == rt->input &&
- state->routing.output == rt->output)
- break;
- state->routing = *rt;
- /* check if the tuner input is used */
- for (i = 0; i < 5; i++) {
- if (((rt->input >> (4 + i * 4)) & 0xf) == 0)
- extern_input = 0;
- }
- state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT;
- state->rxsubchans = V4L2_TUNER_SUB_STEREO;
- msp_set_scart(client, sc_in, 0);
- msp_set_scart(client, sc1_out, 1);
- msp_set_scart(client, sc2_out, 2);
- msp_set_audmode(client);
- reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb;
- val = msp_read_dem(client, reg);
- msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
- /* wake thread when a new input is chosen */
+static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int update = state->radio || state->v4l2_std != id;
+
+ state->v4l2_std = id;
+ state->radio = 0;
+ if (update)
msp_wake_thread(client);
- break;
+ return 0;
+}
+
+static int msp_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int tuner = (rt->input >> 3) & 1;
+ int sc_in = rt->input & 0x7;
+ int sc1_out = rt->output & 0xf;
+ int sc2_out = (rt->output >> 4) & 0xf;
+ u16 val, reg;
+ int i;
+ int extern_input = 1;
+
+ if (state->routing.input == rt->input &&
+ state->routing.output == rt->output)
+ return 0;
+ state->routing = *rt;
+ /* check if the tuner input is used */
+ for (i = 0; i < 5; i++) {
+ if (((rt->input >> (4 + i * 4)) & 0xf) == 0)
+ extern_input = 0;
}
+ state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT;
+ state->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ msp_set_scart(client, sc_in, 0);
+ msp_set_scart(client, sc1_out, 1);
+ msp_set_scart(client, sc2_out, 2);
+ msp_set_audmode(client);
+ reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb;
+ val = msp_read_dem(client, reg);
+ msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
+ /* wake thread when a new input is chosen */
+ msp_wake_thread(client);
+ return 0;
+}
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = arg;
+static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (state->radio)
- break;
- if (state->opmode == OPMODE_AUTOSELECT)
- msp_detect_stereo(client);
- vt->audmode = state->audmode;
- vt->rxsubchans = state->rxsubchans;
- vt->capability |= V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
- break;
- }
+ if (state->radio)
+ return 0;
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_detect_stereo(client);
+ vt->audmode = state->audmode;
+ vt->rxsubchans = state->rxsubchans;
+ vt->capability |= V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ return 0;
+}
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
+static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (state->radio) /* TODO: add mono/stereo support for radio */
- break;
- if (state->audmode == vt->audmode)
- break;
- state->audmode = vt->audmode;
- /* only set audmode */
- msp_set_audmode(client);
- break;
- }
+ if (state->radio) /* TODO: add mono/stereo support for radio */
+ return 0;
+ if (state->audmode == vt->audmode)
+ return 0;
+ state->audmode = vt->audmode;
+ /* only set audmode */
+ msp_set_audmode(client);
+ return 0;
+}
- case VIDIOC_INT_I2S_CLOCK_FREQ:
- {
- u32 *a = (u32 *)arg;
+static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", *a);
+ v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq);
- switch (*a) {
+ switch (freq) {
case 1024000:
state->i2s_mode = 0;
break;
@@ -695,24 +704,24 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
break;
default:
return -EINVAL;
- }
- break;
}
+ return 0;
+}
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
+static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ struct msp_state *state = to_state(sd);
- switch (qc->id) {
+ switch (qc->id) {
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
return v4l2_ctrl_query_fill_std(qc);
default:
break;
- }
- if (!state->has_sound_processing)
- return -EINVAL;
- switch (qc->id) {
+ }
+ if (!state->has_sound_processing)
+ return -EINVAL;
+ switch (qc->id) {
case V4L2_CID_AUDIO_LOUDNESS:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
@@ -720,32 +729,38 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
return v4l2_ctrl_query_fill_std(qc);
default:
return -EINVAL;
- }
}
+ return 0;
+}
- case VIDIOC_G_CTRL:
- return msp_get_ctrl(client, arg);
-
- case VIDIOC_S_CTRL:
- return msp_set_ctrl(client, arg);
+static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_LOG_STATUS:
- {
- const char *p;
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident,
+ (state->rev1 << 16) | state->rev2);
+}
- if (state->opmode == OPMODE_AUTOSELECT)
- msp_detect_stereo(client);
- v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
- client->name, state->rev1, state->rev2);
- v4l_info(client, "Audio: volume %d%s\n",
- state->volume, state->muted ? " (muted)" : "");
- if (state->has_sound_processing) {
- v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n",
- state->balance, state->bass,
- state->treble,
- state->loudness ? "on" : "off");
- }
- switch (state->mode) {
+static int msp_log_status(struct v4l2_subdev *sd)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ const char *p;
+
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_detect_stereo(client);
+ v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
+ client->name, state->rev1, state->rev2);
+ v4l_info(client, "Audio: volume %d%s\n",
+ state->volume, state->muted ? " (muted)" : "");
+ if (state->has_sound_processing) {
+ v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n",
+ state->balance, state->bass,
+ state->treble,
+ state->loudness ? "on" : "off");
+ }
+ switch (state->mode) {
case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono/stereo"; break;
@@ -756,36 +771,25 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
case MSP_MODE_BTSC: p = "BTSC"; break;
case MSP_MODE_EXTERN: p = "External input"; break;
default: p = "unknown"; break;
- }
- if (state->mode == MSP_MODE_EXTERN) {
- v4l_info(client, "Mode: %s\n", p);
- } else if (state->opmode == OPMODE_MANUAL) {
- v4l_info(client, "Mode: %s (%s%s)\n", p,
+ }
+ if (state->mode == MSP_MODE_EXTERN) {
+ v4l_info(client, "Mode: %s\n", p);
+ } else if (state->opmode == OPMODE_MANUAL) {
+ v4l_info(client, "Mode: %s (%s%s)\n", p,
(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
- } else {
- if (state->opmode == OPMODE_AUTODETECT)
- v4l_info(client, "Mode: %s\n", p);
- v4l_info(client, "Standard: %s (%s%s)\n",
+ } else {
+ if (state->opmode == OPMODE_AUTODETECT)
+ v4l_info(client, "Mode: %s\n", p);
+ v4l_info(client, "Standard: %s (%s%s)\n",
msp_standard_std_name(state->std),
(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
- }
- v4l_info(client, "Audmode: 0x%04x\n", state->audmode);
- v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n",
- state->routing.input, state->routing.output);
- v4l_info(client, "ACB: 0x%04x\n", state->acb);
- break;
- }
-
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->ident,
- (state->rev1 << 16) | state->rev2);
-
- default:
- /* unknown */
- return -EINVAL;
}
+ v4l_info(client, "Audmode: 0x%04x\n", state->audmode);
+ v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n",
+ state->routing.input, state->routing.output);
+ v4l_info(client, "ACB: 0x%04x\n", state->acb);
return 0;
}
@@ -803,11 +807,49 @@ static int msp_resume(struct i2c_client *client)
return 0;
}
+static int msp_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops msp_core_ops = {
+ .log_status = msp_log_status,
+ .g_chip_ident = msp_g_chip_ident,
+ .g_ctrl = msp_g_ctrl,
+ .s_ctrl = msp_s_ctrl,
+ .queryctrl = msp_queryctrl,
+#ifdef CONFIG_VIDEO_ALLOW_V4L1
+ .ioctl = msp_ioctl,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops msp_tuner_ops = {
+ .s_frequency = msp_s_frequency,
+ .g_tuner = msp_g_tuner,
+ .s_tuner = msp_s_tuner,
+ .s_radio = msp_s_radio,
+ .s_std = msp_s_std,
+};
+
+static const struct v4l2_subdev_audio_ops msp_audio_ops = {
+ .s_routing = msp_s_routing,
+ .s_i2s_clock_freq = msp_s_i2s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops msp_ops = {
+ .core = &msp_core_ops,
+ .tuner = &msp_tuner_ops,
+ .audio = &msp_audio_ops,
+};
+
/* ----------------------------------------------------------------------- */
static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct msp_state *state;
+ struct v4l2_subdev *sd;
int (*thread_func)(void *data) = NULL;
int msp_hard;
int msp_family;
@@ -827,7 +869,8 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (!state)
return -ENOMEM;
- i2c_set_clientdata(client, state);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &msp_ops);
state->v4l2_std = V4L2_STD_NTSC;
state->audmode = V4L2_TUNER_MODE_STEREO;
@@ -972,8 +1015,9 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
static int msp_remove(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ v4l2_device_unregister_subdev(&state->sd);
/* shutdown control thread */
if (state->kthread) {
state->restart = 1;
diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h
index ab69a290e5dc..3fe1c1b10f53 100644
--- a/drivers/media/video/msp3400-driver.h
+++ b/drivers/media/video/msp3400-driver.h
@@ -5,6 +5,7 @@
#define MSP3400_DRIVER_H
#include <media/msp3400.h>
+#include <media/v4l2-device.h>
/* ---------------------------------------------------------------------- */
@@ -49,6 +50,7 @@ extern int msp_dolby;
extern int msp_stereo_thresh;
struct msp_state {
+ struct v4l2_subdev sd;
int rev1, rev2;
int ident;
u8 has_nicam;
@@ -96,6 +98,11 @@ struct msp_state {
unsigned int watch_stereo:1;
};
+static inline struct msp_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct msp_state, sd);
+}
+
/* msp3400-driver.c */
int msp_write_dem(struct i2c_client *client, int addr, int val);
int msp_write_dsp(struct i2c_client *client, int addr, int val);
diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c
index 846a14a61fd1..a655e9c30146 100644
--- a/drivers/media/video/msp3400-kthreads.c
+++ b/drivers/media/video/msp3400-kthreads.c
@@ -159,7 +159,7 @@ const char *msp_standard_std_name(int std)
static void msp_set_source(struct i2c_client *client, u16 src)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (msp_dolby) {
msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */
@@ -186,7 +186,7 @@ void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2)
void msp3400c_set_mode(struct i2c_client *client, int mode)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode];
int tuner = (state->routing.input >> 3) & 1;
int i;
@@ -227,7 +227,7 @@ static void msp3400c_set_audmode(struct i2c_client *client)
static char *strmode[] = {
"mono", "stereo", "lang2", "lang1", "lang1+lang2"
};
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
char *modestr = (state->audmode >= 0 && state->audmode < 5) ?
strmode[state->audmode] : "unknown";
int src = 0; /* channel source: FM/AM, nicam or SCART */
@@ -356,7 +356,7 @@ static void msp3400c_set_audmode(struct i2c_client *client)
static void msp3400c_print_mode(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (state->main == state->second)
v4l_dbg(1, msp_debug, client,
@@ -385,7 +385,7 @@ static void msp3400c_print_mode(struct i2c_client *client)
static int msp3400c_detect_stereo(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int val;
int rxsubchans = state->rxsubchans;
int newnicam = state->nicam_on;
@@ -463,7 +463,7 @@ static int msp3400c_detect_stereo(struct i2c_client *client)
/* stereo/multilang monitoring */
static void watch_stereo(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (msp_detect_stereo(client))
msp_set_audmode(client);
@@ -475,7 +475,7 @@ static void watch_stereo(struct i2c_client *client)
int msp3400c_thread(void *data)
{
struct i2c_client *client = data;
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
struct msp3400c_carrier_detect *cd;
int count, max1, max2, val1, val2, val, i;
@@ -659,7 +659,7 @@ no_second:
int msp3410d_thread(void *data)
{
struct i2c_client *client = data;
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int val, i, std, count;
v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n");
@@ -825,7 +825,7 @@ restart:
static int msp34xxg_modus(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (state->radio) {
v4l_dbg(1, msp_debug, client, "selected radio modus\n");
@@ -852,7 +852,7 @@ static int msp34xxg_modus(struct i2c_client *client)
static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int source, matrix;
switch (state->audmode) {
@@ -895,7 +895,7 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in)
static void msp34xxg_set_sources(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
u32 in = state->routing.input;
msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf);
@@ -911,7 +911,7 @@ static void msp34xxg_set_sources(struct i2c_client *client)
/* (re-)initialize the msp34xxg */
static void msp34xxg_reset(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int tuner = (state->routing.input >> 3) & 1;
int modus;
@@ -954,7 +954,7 @@ static void msp34xxg_reset(struct i2c_client *client)
int msp34xxg_thread(void *data)
{
struct i2c_client *client = data;
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int val, i;
v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n");
@@ -1049,7 +1049,7 @@ unmute:
static int msp34xxg_detect_stereo(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int status = msp_read_dem(client, 0x0200);
int is_bilingual = status & 0x100;
int is_stereo = status & 0x40;
@@ -1078,7 +1078,7 @@ static int msp34xxg_detect_stereo(struct i2c_client *client)
static void msp34xxg_set_audmode(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (state->std == 0x20) {
if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) &&
@@ -1095,7 +1095,7 @@ static void msp34xxg_set_audmode(struct i2c_client *client)
void msp_set_audmode(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
switch (state->opmode) {
case OPMODE_MANUAL:
@@ -1110,7 +1110,7 @@ void msp_set_audmode(struct i2c_client *client)
int msp_detect_stereo(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
switch (state->opmode) {
case OPMODE_MANUAL:
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index 0c524376b67e..1a1a12453672 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -272,21 +272,20 @@ static int mt9m001_set_bus_param(struct soc_camera_device *icd,
static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
{
struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
- unsigned int width_flag = SOCAM_DATAWIDTH_10;
+ struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
+ /* MT9M001 has all capture_format parameters fixed */
+ unsigned long flags = SOCAM_DATAWIDTH_10 | SOCAM_PCLK_SAMPLE_RISING |
+ SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
+ SOCAM_MASTER;
if (bus_switch_possible(mt9m001))
- width_flag |= SOCAM_DATAWIDTH_8;
+ flags |= SOCAM_DATAWIDTH_8;
- /* MT9M001 has all capture_format parameters fixed */
- return SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_MASTER |
- width_flag;
+ return soc_camera_apply_sensor_flags(icl, flags);
}
-static int mt9m001_set_fmt_cap(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect)
+static int mt9m001_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
{
struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
int ret;
@@ -298,7 +297,7 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd,
ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank);
/* The caller provides a supported format, as verified per
- * call to icd->try_fmt_cap() */
+ * call to icd->try_fmt() */
if (!ret)
ret = reg_write(icd, MT9M001_COLUMN_START, rect->left);
if (!ret)
@@ -325,18 +324,20 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd,
return ret;
}
-static int mt9m001_try_fmt_cap(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9m001_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
{
- if (f->fmt.pix.height < 32 + icd->y_skip_top)
- f->fmt.pix.height = 32 + icd->y_skip_top;
- if (f->fmt.pix.height > 1024 + icd->y_skip_top)
- f->fmt.pix.height = 1024 + icd->y_skip_top;
- if (f->fmt.pix.width < 48)
- f->fmt.pix.width = 48;
- if (f->fmt.pix.width > 1280)
- f->fmt.pix.width = 1280;
- f->fmt.pix.width &= ~0x01; /* has to be even, unsure why was ~3 */
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (pix->height < 32 + icd->y_skip_top)
+ pix->height = 32 + icd->y_skip_top;
+ if (pix->height > 1024 + icd->y_skip_top)
+ pix->height = 1024 + icd->y_skip_top;
+ if (pix->width < 48)
+ pix->width = 48;
+ if (pix->width > 1280)
+ pix->width = 1280;
+ pix->width &= ~0x01; /* has to be even, unsure why was ~3 */
return 0;
}
@@ -447,8 +448,8 @@ static struct soc_camera_ops mt9m001_ops = {
.release = mt9m001_release,
.start_capture = mt9m001_start_capture,
.stop_capture = mt9m001_stop_capture,
- .set_fmt_cap = mt9m001_set_fmt_cap,
- .try_fmt_cap = mt9m001_try_fmt_cap,
+ .set_fmt = mt9m001_set_fmt,
+ .try_fmt = mt9m001_try_fmt,
.set_bus_param = mt9m001_set_bus_param,
.query_bus_param = mt9m001_query_bus_param,
.controls = mt9m001_controls,
@@ -578,6 +579,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro
static int mt9m001_video_probe(struct soc_camera_device *icd)
{
struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+ struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
s32 data;
int ret;
@@ -588,7 +590,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
return -ENODEV;
/* Enable the chip */
- data = reg_write(&mt9m001->icd, MT9M001_CHIP_ENABLE, 1);
+ data = reg_write(icd, MT9M001_CHIP_ENABLE, 1);
dev_dbg(&icd->dev, "write: %d\n", data);
/* Read out the chip version register */
@@ -600,7 +602,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
case 0x8421:
mt9m001->model = V4L2_IDENT_MT9M001C12ST;
icd->formats = mt9m001_colour_formats;
- if (mt9m001->client->dev.platform_data)
+ if (gpio_is_valid(icl->gpio))
icd->num_formats = ARRAY_SIZE(mt9m001_colour_formats);
else
icd->num_formats = 1;
@@ -608,7 +610,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
case 0x8431:
mt9m001->model = V4L2_IDENT_MT9M001C12STM;
icd->formats = mt9m001_monochrome_formats;
- if (mt9m001->client->dev.platform_data)
+ if (gpio_is_valid(icl->gpio))
icd->num_formats = ARRAY_SIZE(mt9m001_monochrome_formats);
else
icd->num_formats = 1;
@@ -640,8 +642,8 @@ static void mt9m001_video_remove(struct soc_camera_device *icd)
struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m001->client->addr,
- mt9m001->icd.dev.parent, mt9m001->icd.vdev);
- soc_camera_video_stop(&mt9m001->icd);
+ icd->dev.parent, icd->vdev);
+ soc_camera_video_stop(icd);
}
static int mt9m001_probe(struct i2c_client *client,
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
index da0b2d553fd0..c89ea41fe259 100644
--- a/drivers/media/video/mt9m111.c
+++ b/drivers/media/video/mt9m111.c
@@ -1,5 +1,5 @@
/*
- * Driver for MT9M111 CMOS Image Sensor from Micron
+ * Driver for MT9M111/MT9M112 CMOS Image Sensor from Micron
*
* Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
*
@@ -19,7 +19,7 @@
#include <media/soc_camera.h>
/*
- * mt9m111 i2c address is 0x5d or 0x48 (depending on SAddr pin)
+ * mt9m111 and mt9m112 i2c address is 0x5d or 0x48 (depending on SAddr pin)
* The platform has to define i2c_board_info and call i2c_register_board_info()
*/
@@ -90,7 +90,7 @@
#define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b
#define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14)
-
+#define MT9M111_OPMODE_AUTOWHITEBAL_EN (1 << 1)
#define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14)
#define MT9M111_OUTFMT_BYPASS_IFP (1 << 10)
@@ -128,9 +128,14 @@
.colorspace = _colorspace }
#define RGB_FMT(_name, _depth, _fourcc) \
COL_FMT(_name, _depth, _fourcc, V4L2_COLORSPACE_SRGB)
+#define JPG_FMT(_name, _depth, _fourcc) \
+ COL_FMT(_name, _depth, _fourcc, V4L2_COLORSPACE_JPEG)
static const struct soc_camera_data_format mt9m111_colour_formats[] = {
- COL_FMT("YCrYCb 8 bit", 8, V4L2_PIX_FMT_YUYV, V4L2_COLORSPACE_JPEG),
+ JPG_FMT("CbYCrY 16 bit", 16, V4L2_PIX_FMT_UYVY),
+ JPG_FMT("CrYCbY 16 bit", 16, V4L2_PIX_FMT_VYUY),
+ JPG_FMT("YCbYCr 16 bit", 16, V4L2_PIX_FMT_YUYV),
+ JPG_FMT("YCrYCb 16 bit", 16, V4L2_PIX_FMT_YVYU),
RGB_FMT("RGB 565", 16, V4L2_PIX_FMT_RGB565),
RGB_FMT("RGB 555", 16, V4L2_PIX_FMT_RGB555),
RGB_FMT("Bayer (sRGB) 10 bit", 10, V4L2_PIX_FMT_SBGGR16),
@@ -145,7 +150,7 @@ enum mt9m111_context {
struct mt9m111 {
struct i2c_client *client;
struct soc_camera_device icd;
- int model; /* V4L2_IDENT_MT9M111* codes from v4l2-chip-ident.h */
+ int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */
enum mt9m111_context context;
unsigned int left, top, width, height;
u32 pixfmt;
@@ -158,6 +163,7 @@ struct mt9m111 {
unsigned int swap_rgb_red_blue:1;
unsigned int swap_yuv_y_chromas:1;
unsigned int swap_yuv_cb_cr:1;
+ unsigned int autowhitebalance:1;
};
static int reg_page_map_set(struct i2c_client *client, const u16 reg)
@@ -410,9 +416,13 @@ static int mt9m111_stop_capture(struct soc_camera_device *icd)
static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd)
{
- return SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING |
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct soc_camera_link *icl = mt9m111->client->dev.platform_data;
+ unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING |
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
SOCAM_DATAWIDTH_8;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
}
static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f)
@@ -438,7 +448,24 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt)
case V4L2_PIX_FMT_RGB565:
ret = mt9m111_setfmt_rgb565(icd);
break;
+ case V4L2_PIX_FMT_UYVY:
+ mt9m111->swap_yuv_y_chromas = 0;
+ mt9m111->swap_yuv_cb_cr = 0;
+ ret = mt9m111_setfmt_yuv(icd);
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ mt9m111->swap_yuv_y_chromas = 0;
+ mt9m111->swap_yuv_cb_cr = 1;
+ ret = mt9m111_setfmt_yuv(icd);
+ break;
case V4L2_PIX_FMT_YUYV:
+ mt9m111->swap_yuv_y_chromas = 1;
+ mt9m111->swap_yuv_cb_cr = 0;
+ ret = mt9m111_setfmt_yuv(icd);
+ break;
+ case V4L2_PIX_FMT_YVYU:
+ mt9m111->swap_yuv_y_chromas = 1;
+ mt9m111->swap_yuv_cb_cr = 1;
ret = mt9m111_setfmt_yuv(icd);
break;
default:
@@ -452,8 +479,8 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt)
return ret;
}
-static int mt9m111_set_fmt_cap(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect)
+static int mt9m111_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
{
struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
int ret;
@@ -473,13 +500,15 @@ static int mt9m111_set_fmt_cap(struct soc_camera_device *icd,
return ret;
}
-static int mt9m111_try_fmt_cap(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9m111_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
{
- if (f->fmt.pix.height > MT9M111_MAX_HEIGHT)
- f->fmt.pix.height = MT9M111_MAX_HEIGHT;
- if (f->fmt.pix.width > MT9M111_MAX_WIDTH)
- f->fmt.pix.width = MT9M111_MAX_WIDTH;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (pix->height > MT9M111_MAX_HEIGHT)
+ pix->height = MT9M111_MAX_HEIGHT;
+ if (pix->width > MT9M111_MAX_WIDTH)
+ pix->width = MT9M111_MAX_WIDTH;
return 0;
}
@@ -597,8 +626,8 @@ static struct soc_camera_ops mt9m111_ops = {
.release = mt9m111_release,
.start_capture = mt9m111_start_capture,
.stop_capture = mt9m111_stop_capture,
- .set_fmt_cap = mt9m111_set_fmt_cap,
- .try_fmt_cap = mt9m111_try_fmt_cap,
+ .set_fmt = mt9m111_set_fmt,
+ .try_fmt = mt9m111_try_fmt,
.query_bus_param = mt9m111_query_bus_param,
.set_bus_param = mt9m111_set_bus_param,
.controls = mt9m111_controls,
@@ -634,18 +663,15 @@ static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask)
static int mt9m111_get_global_gain(struct soc_camera_device *icd)
{
- unsigned int data, gain;
+ int data;
data = reg_read(GLOBAL_GAIN);
if (data >= 0)
- gain = ((data & (1 << 10)) * 2)
- | ((data & (1 << 9)) * 2)
- | (data & 0x2f);
- else
- gain = data;
-
- return gain;
+ return (data & 0x2f) * (1 << ((data >> 10) & 1)) *
+ (1 << ((data >> 9) & 1));
+ return data;
}
+
static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain)
{
u16 val;
@@ -679,6 +705,23 @@ static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on)
return ret;
}
+
+static int mt9m111_set_autowhitebalance(struct soc_camera_device *icd, int on)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int ret;
+
+ if (on)
+ ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
+ else
+ ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
+
+ if (!ret)
+ mt9m111->autowhitebalance = on;
+
+ return ret;
+}
+
static int mt9m111_get_control(struct soc_camera_device *icd,
struct v4l2_control *ctrl)
{
@@ -715,6 +758,9 @@ static int mt9m111_get_control(struct soc_camera_device *icd,
case V4L2_CID_EXPOSURE_AUTO:
ctrl->value = mt9m111->autoexposure;
break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ctrl->value = mt9m111->autowhitebalance;
+ break;
}
return 0;
}
@@ -748,6 +794,9 @@ static int mt9m111_set_control(struct soc_camera_device *icd,
case V4L2_CID_EXPOSURE_AUTO:
ret = mt9m111_set_autoexposure(icd, ctrl->value);
break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = mt9m111_set_autowhitebalance(icd, ctrl->value);
+ break;
default:
ret = -EINVAL;
}
@@ -766,6 +815,7 @@ static int mt9m111_restore_state(struct soc_camera_device *icd)
mt9m111_set_flip(icd, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
mt9m111_set_global_gain(icd, icd->gain);
mt9m111_set_autoexposure(icd, mt9m111->autoexposure);
+ mt9m111_set_autowhitebalance(icd, mt9m111->autowhitebalance);
return 0;
}
@@ -798,7 +848,7 @@ static int mt9m111_init(struct soc_camera_device *icd)
if (!ret)
ret = mt9m111_set_autoexposure(icd, mt9m111->autoexposure);
if (ret)
- dev_err(&icd->dev, "mt9m111 init failed: %d\n", ret);
+ dev_err(&icd->dev, "mt9m11x init failed: %d\n", ret);
return ret;
}
@@ -808,7 +858,7 @@ static int mt9m111_release(struct soc_camera_device *icd)
ret = mt9m111_disable(icd);
if (ret < 0)
- dev_err(&icd->dev, "mt9m111 release failed: %d\n", ret);
+ dev_err(&icd->dev, "mt9m11x release failed: %d\n", ret);
return ret;
}
@@ -841,25 +891,30 @@ static int mt9m111_video_probe(struct soc_camera_device *icd)
data = reg_read(CHIP_VERSION);
switch (data) {
- case 0x143a:
+ case 0x143a: /* MT9M111 */
mt9m111->model = V4L2_IDENT_MT9M111;
- icd->formats = mt9m111_colour_formats;
- icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats);
+ break;
+ case 0x148c: /* MT9M112 */
+ mt9m111->model = V4L2_IDENT_MT9M112;
break;
default:
ret = -ENODEV;
dev_err(&icd->dev,
- "No MT9M111 chip detected, register read %x\n", data);
+ "No MT9M11x chip detected, register read %x\n", data);
goto ei2c;
}
- dev_info(&icd->dev, "Detected a MT9M111 chip ID 0x143a\n");
+ icd->formats = mt9m111_colour_formats;
+ icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats);
+
+ dev_info(&icd->dev, "Detected a MT9M11x chip ID %x\n", data);
ret = soc_camera_video_start(icd);
if (ret)
goto eisis;
mt9m111->autoexposure = 1;
+ mt9m111->autowhitebalance = 1;
mt9m111->swap_rgb_even_odd = 1;
mt9m111->swap_rgb_red_blue = 1;
@@ -889,7 +944,7 @@ static int mt9m111_probe(struct i2c_client *client,
int ret;
if (!icl) {
- dev_err(&client->dev, "MT9M111 driver needs platform data\n");
+ dev_err(&client->dev, "MT9M11x driver needs platform data\n");
return -EINVAL;
}
@@ -968,6 +1023,6 @@ static void __exit mt9m111_mod_exit(void)
module_init(mt9m111_mod_init);
module_exit(mt9m111_mod_exit);
-MODULE_DESCRIPTION("Micron MT9M111 Camera driver");
+MODULE_DESCRIPTION("Micron MT9M111/MT9M112 Camera driver");
MODULE_AUTHOR("Robert Jarzmik");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
new file mode 100644
index 000000000000..1a9d53966d06
--- /dev/null
+++ b/drivers/media/video/mt9t031.c
@@ -0,0 +1,736 @@
+/*
+ * Driver for MT9T031 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering <lg@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/soc_camera.h>
+
+/* mt9t031 i2c address 0x5d
+ * The platform has to define i2c_board_info
+ * and call i2c_register_board_info() */
+
+/* mt9t031 selected register addresses */
+#define MT9T031_CHIP_VERSION 0x00
+#define MT9T031_ROW_START 0x01
+#define MT9T031_COLUMN_START 0x02
+#define MT9T031_WINDOW_HEIGHT 0x03
+#define MT9T031_WINDOW_WIDTH 0x04
+#define MT9T031_HORIZONTAL_BLANKING 0x05
+#define MT9T031_VERTICAL_BLANKING 0x06
+#define MT9T031_OUTPUT_CONTROL 0x07
+#define MT9T031_SHUTTER_WIDTH_UPPER 0x08
+#define MT9T031_SHUTTER_WIDTH 0x09
+#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a
+#define MT9T031_FRAME_RESTART 0x0b
+#define MT9T031_SHUTTER_DELAY 0x0c
+#define MT9T031_RESET 0x0d
+#define MT9T031_READ_MODE_1 0x1e
+#define MT9T031_READ_MODE_2 0x20
+#define MT9T031_READ_MODE_3 0x21
+#define MT9T031_ROW_ADDRESS_MODE 0x22
+#define MT9T031_COLUMN_ADDRESS_MODE 0x23
+#define MT9T031_GLOBAL_GAIN 0x35
+#define MT9T031_CHIP_ENABLE 0xF8
+
+#define MT9T031_MAX_HEIGHT 1536
+#define MT9T031_MAX_WIDTH 2048
+#define MT9T031_MIN_HEIGHT 2
+#define MT9T031_MIN_WIDTH 2
+#define MT9T031_HORIZONTAL_BLANK 142
+#define MT9T031_VERTICAL_BLANK 25
+#define MT9T031_COLUMN_SKIP 32
+#define MT9T031_ROW_SKIP 20
+
+#define MT9T031_BUS_PARAM (SOCAM_PCLK_SAMPLE_RISING | \
+ SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | \
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | \
+ SOCAM_MASTER | SOCAM_DATAWIDTH_10)
+
+static const struct soc_camera_data_format mt9t031_colour_formats[] = {
+ {
+ .name = "Bayer (sRGB) 10 bit",
+ .depth = 10,
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ }
+};
+
+struct mt9t031 {
+ struct i2c_client *client;
+ struct soc_camera_device icd;
+ int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
+ unsigned char autoexposure;
+ u16 xskip;
+ u16 yskip;
+};
+
+static int reg_read(struct soc_camera_device *icd, const u8 reg)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ struct i2c_client *client = mt9t031->client;
+ s32 data = i2c_smbus_read_word_data(client, reg);
+ return data < 0 ? data : swab16(data);
+}
+
+static int reg_write(struct soc_camera_device *icd, const u8 reg,
+ const u16 data)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ return i2c_smbus_write_word_data(mt9t031->client, reg, swab16(data));
+}
+
+static int reg_set(struct soc_camera_device *icd, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(icd, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(icd, reg, ret | data);
+}
+
+static int reg_clear(struct soc_camera_device *icd, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(icd, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(icd, reg, ret & ~data);
+}
+
+static int set_shutter(struct soc_camera_device *icd, const u32 data)
+{
+ int ret;
+
+ ret = reg_write(icd, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16);
+
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_SHUTTER_WIDTH, data & 0xffff);
+
+ return ret;
+}
+
+static int get_shutter(struct soc_camera_device *icd, u32 *data)
+{
+ int ret;
+
+ ret = reg_read(icd, MT9T031_SHUTTER_WIDTH_UPPER);
+ *data = ret << 16;
+
+ if (ret >= 0)
+ ret = reg_read(icd, MT9T031_SHUTTER_WIDTH);
+ *data |= ret & 0xffff;
+
+ return ret < 0 ? ret : 0;
+}
+
+static int mt9t031_init(struct soc_camera_device *icd)
+{
+ int ret;
+
+ /* Disable chip output, synchronous option update */
+ dev_dbg(icd->vdev->parent, "%s\n", __func__);
+
+ ret = reg_write(icd, MT9T031_RESET, 1);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_RESET, 0);
+ if (ret >= 0)
+ ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3);
+
+ return ret >= 0 ? 0 : -EIO;
+}
+
+static int mt9t031_release(struct soc_camera_device *icd)
+{
+ /* Disable the chip */
+ reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3);
+ return 0;
+}
+
+static int mt9t031_start_capture(struct soc_camera_device *icd)
+{
+ /* Switch to master "normal" mode */
+ if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 3) < 0)
+ return -EIO;
+ return 0;
+}
+
+static int mt9t031_stop_capture(struct soc_camera_device *icd)
+{
+ /* Stop sensor readout */
+ if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3) < 0)
+ return -EIO;
+ return 0;
+}
+
+static int mt9t031_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+{
+ /* The caller should have queried our parameters, check anyway */
+ if (flags & ~MT9T031_BUS_PARAM)
+ return -EINVAL;
+
+ if (flags & SOCAM_PCLK_SAMPLE_FALLING)
+ reg_set(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+ else
+ reg_clear(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+
+ return 0;
+}
+
+static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ struct soc_camera_link *icl = mt9t031->client->dev.platform_data;
+
+ return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM);
+}
+
+static int mt9t031_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ int ret;
+ const u16 hblank = MT9T031_HORIZONTAL_BLANK,
+ vblank = MT9T031_VERTICAL_BLANK;
+ u16 xbin, xskip = mt9t031->xskip, ybin, yskip = mt9t031->yskip,
+ width = rect->width * xskip, height = rect->height * yskip;
+
+ if (pixfmt) {
+ /* S_FMT - use binning and skipping for scaling, recalculate */
+ /* Is this more optimal than just a division? */
+ for (xskip = 8; xskip > 1; xskip--)
+ if (rect->width * xskip <= icd->width_max)
+ break;
+
+ for (yskip = 8; yskip > 1; yskip--)
+ if (rect->height * yskip <= icd->height_max)
+ break;
+
+ width = rect->width * xskip;
+ height = rect->height * yskip;
+
+ dev_dbg(&icd->dev, "xskip %u, width %u, yskip %u, height %u\n",
+ xskip, width, yskip, height);
+ }
+
+ xbin = min(xskip, (u16)3);
+ ybin = min(yskip, (u16)3);
+
+ /* Make sure we don't exceed frame limits */
+ if (rect->left + width > icd->width_max)
+ rect->left = (icd->width_max - width) / 2;
+
+ if (rect->top + height > icd->height_max)
+ rect->top = (icd->height_max - height) / 2;
+
+ /* Could just do roundup(rect->left, [xy]bin); but this is cheaper */
+ switch (xbin) {
+ case 2:
+ rect->left = (rect->left + 1) & ~1;
+ break;
+ case 3:
+ rect->left = roundup(rect->left, 3);
+ }
+
+ switch (ybin) {
+ case 2:
+ rect->top = (rect->top + 1) & ~1;
+ break;
+ case 3:
+ rect->top = roundup(rect->top, 3);
+ }
+
+ /* Blanking and start values - default... */
+ ret = reg_write(icd, MT9T031_HORIZONTAL_BLANKING, hblank);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_VERTICAL_BLANKING, vblank);
+
+ if (pixfmt) {
+ /* Binning, skipping */
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_COLUMN_ADDRESS_MODE,
+ ((xbin - 1) << 4) | (xskip - 1));
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_ROW_ADDRESS_MODE,
+ ((ybin - 1) << 4) | (yskip - 1));
+ }
+ dev_dbg(&icd->dev, "new left %u, top %u\n", rect->left, rect->top);
+
+ /* The caller provides a supported format, as guaranteed by
+ * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_COLUMN_START, rect->left);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_ROW_START, rect->top);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_WINDOW_WIDTH, width - 1);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_WINDOW_HEIGHT,
+ height + icd->y_skip_top - 1);
+ if (ret >= 0 && mt9t031->autoexposure) {
+ ret = set_shutter(icd, height + icd->y_skip_top + vblank);
+ if (ret >= 0) {
+ const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
+ const struct v4l2_queryctrl *qctrl =
+ soc_camera_find_qctrl(icd->ops,
+ V4L2_CID_EXPOSURE);
+ icd->exposure = (shutter_max / 2 + (height +
+ icd->y_skip_top + vblank - 1) *
+ (qctrl->maximum - qctrl->minimum)) /
+ shutter_max + qctrl->minimum;
+ }
+ }
+
+ if (!ret && pixfmt) {
+ mt9t031->xskip = xskip;
+ mt9t031->yskip = yskip;
+ }
+
+ return ret < 0 ? ret : 0;
+}
+
+static int mt9t031_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (pix->height < icd->height_min)
+ pix->height = icd->height_min;
+ if (pix->height > icd->height_max)
+ pix->height = icd->height_max;
+ if (pix->width < icd->width_min)
+ pix->width = icd->width_min;
+ if (pix->width > icd->width_max)
+ pix->width = icd->width_max;
+
+ pix->width &= ~0x01; /* has to be even */
+ pix->height &= ~0x01; /* has to be even */
+
+ return 0;
+}
+
+static int mt9t031_get_chip_id(struct soc_camera_device *icd,
+ struct v4l2_chip_ident *id)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+
+ if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match_chip != mt9t031->client->addr)
+ return -ENODEV;
+
+ id->ident = mt9t031->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9t031_get_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+
+ if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match_chip != mt9t031->client->addr)
+ return -ENODEV;
+
+ reg->val = reg_read(icd, reg->reg);
+
+ if (reg->val > 0xffff)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt9t031_set_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+
+ if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match_chip != mt9t031->client->addr)
+ return -ENODEV;
+
+ if (reg_write(icd, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static const struct v4l2_queryctrl mt9t031_controls[] = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Vertically",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ }, {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ }, {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 1,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 255,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ }, {
+ .id = V4L2_CID_EXPOSURE_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ }
+};
+
+static int mt9t031_video_probe(struct soc_camera_device *);
+static void mt9t031_video_remove(struct soc_camera_device *);
+static int mt9t031_get_control(struct soc_camera_device *, struct v4l2_control *);
+static int mt9t031_set_control(struct soc_camera_device *, struct v4l2_control *);
+
+static struct soc_camera_ops mt9t031_ops = {
+ .owner = THIS_MODULE,
+ .probe = mt9t031_video_probe,
+ .remove = mt9t031_video_remove,
+ .init = mt9t031_init,
+ .release = mt9t031_release,
+ .start_capture = mt9t031_start_capture,
+ .stop_capture = mt9t031_stop_capture,
+ .set_fmt = mt9t031_set_fmt,
+ .try_fmt = mt9t031_try_fmt,
+ .set_bus_param = mt9t031_set_bus_param,
+ .query_bus_param = mt9t031_query_bus_param,
+ .controls = mt9t031_controls,
+ .num_controls = ARRAY_SIZE(mt9t031_controls),
+ .get_control = mt9t031_get_control,
+ .set_control = mt9t031_set_control,
+ .get_chip_id = mt9t031_get_chip_id,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .get_register = mt9t031_get_register,
+ .set_register = mt9t031_set_register,
+#endif
+};
+
+static int mt9t031_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ data = reg_read(icd, MT9T031_READ_MODE_2);
+ if (data < 0)
+ return -EIO;
+ ctrl->value = !!(data & 0x8000);
+ break;
+ case V4L2_CID_HFLIP:
+ data = reg_read(icd, MT9T031_READ_MODE_2);
+ if (data < 0)
+ return -EIO;
+ ctrl->value = !!(data & 0x4000);
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ ctrl->value = mt9t031->autoexposure;
+ break;
+ }
+ return 0;
+}
+
+static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ const struct v4l2_queryctrl *qctrl;
+ int data;
+
+ qctrl = soc_camera_find_qctrl(&mt9t031_ops, ctrl->id);
+
+ if (!qctrl)
+ return -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->value)
+ data = reg_set(icd, MT9T031_READ_MODE_2, 0x8000);
+ else
+ data = reg_clear(icd, MT9T031_READ_MODE_2, 0x8000);
+ if (data < 0)
+ return -EIO;
+ break;
+ case V4L2_CID_HFLIP:
+ if (ctrl->value)
+ data = reg_set(icd, MT9T031_READ_MODE_2, 0x4000);
+ else
+ data = reg_clear(icd, MT9T031_READ_MODE_2, 0x4000);
+ if (data < 0)
+ return -EIO;
+ break;
+ case V4L2_CID_GAIN:
+ if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
+ return -EINVAL;
+ /* See Datasheet Table 7, Gain settings. */
+ if (ctrl->value <= qctrl->default_value) {
+ /* Pack it into 0..1 step 0.125, register values 0..8 */
+ unsigned long range = qctrl->default_value - qctrl->minimum;
+ data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
+
+ dev_dbg(&icd->dev, "Setting gain %d\n", data);
+ data = reg_write(icd, MT9T031_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+ } else {
+ /* Pack it into 1.125..15 variable step, register values 9..67 */
+ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
+ unsigned long range = qctrl->maximum - qctrl->default_value - 1;
+ unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
+ 111 + range / 2) / range + 9;
+
+ if (gain <= 32)
+ data = gain;
+ else if (gain <= 64)
+ data = ((gain - 32) * 16 + 16) / 32 + 80;
+ else
+ data = ((gain - 64) * 7 + 28) / 56 + 96;
+
+ dev_dbg(&icd->dev, "Setting gain from %d to %d\n",
+ reg_read(icd, MT9T031_GLOBAL_GAIN), data);
+ data = reg_write(icd, MT9T031_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+ }
+
+ /* Success */
+ icd->gain = ctrl->value;
+ break;
+ case V4L2_CID_EXPOSURE:
+ /* mt9t031 has maximum == default */
+ if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
+ return -EINVAL;
+ else {
+ const unsigned long range = qctrl->maximum - qctrl->minimum;
+ const u32 shutter = ((ctrl->value - qctrl->minimum) * 1048 +
+ range / 2) / range + 1;
+ u32 old;
+
+ get_shutter(icd, &old);
+ dev_dbg(&icd->dev, "Setting shutter width from %u to %u\n",
+ old, shutter);
+ if (set_shutter(icd, shutter) < 0)
+ return -EIO;
+ icd->exposure = ctrl->value;
+ mt9t031->autoexposure = 0;
+ }
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->value) {
+ const u16 vblank = MT9T031_VERTICAL_BLANK;
+ const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
+ if (set_shutter(icd, icd->height +
+ icd->y_skip_top + vblank) < 0)
+ return -EIO;
+ qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
+ icd->exposure = (shutter_max / 2 + (icd->height +
+ icd->y_skip_top + vblank - 1) *
+ (qctrl->maximum - qctrl->minimum)) /
+ shutter_max + qctrl->minimum;
+ mt9t031->autoexposure = 1;
+ } else
+ mt9t031->autoexposure = 0;
+ break;
+ }
+ return 0;
+}
+
+/* Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one */
+static int mt9t031_video_probe(struct soc_camera_device *icd)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ s32 data;
+ int ret;
+
+ /* We must have a parent by now. And it cannot be a wrong one.
+ * So this entire test is completely redundant. */
+ if (!icd->dev.parent ||
+ to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
+ return -ENODEV;
+
+ /* Enable the chip */
+ data = reg_write(icd, MT9T031_CHIP_ENABLE, 1);
+ dev_dbg(&icd->dev, "write: %d\n", data);
+
+ /* Read out the chip version register */
+ data = reg_read(icd, MT9T031_CHIP_VERSION);
+
+ switch (data) {
+ case 0x1621:
+ mt9t031->model = V4L2_IDENT_MT9T031;
+ icd->formats = mt9t031_colour_formats;
+ icd->num_formats = ARRAY_SIZE(mt9t031_colour_formats);
+ break;
+ default:
+ ret = -ENODEV;
+ dev_err(&icd->dev,
+ "No MT9T031 chip detected, register read %x\n", data);
+ goto ei2c;
+ }
+
+ dev_info(&icd->dev, "Detected a MT9T031 chip ID %x\n", data);
+
+ /* Now that we know the model, we can start video */
+ ret = soc_camera_video_start(icd);
+ if (ret)
+ goto evstart;
+
+ return 0;
+
+evstart:
+ei2c:
+ return ret;
+}
+
+static void mt9t031_video_remove(struct soc_camera_device *icd)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+
+ dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9t031->client->addr,
+ icd->dev.parent, icd->vdev);
+ soc_camera_video_stop(icd);
+}
+
+static int mt9t031_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9t031 *mt9t031;
+ struct soc_camera_device *icd;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl = client->dev.platform_data;
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "MT9T031 driver needs platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9t031 = kzalloc(sizeof(struct mt9t031), GFP_KERNEL);
+ if (!mt9t031)
+ return -ENOMEM;
+
+ mt9t031->client = client;
+ i2c_set_clientdata(client, mt9t031);
+
+ /* Second stage probe - when a capture adapter is there */
+ icd = &mt9t031->icd;
+ icd->ops = &mt9t031_ops;
+ icd->control = &client->dev;
+ icd->x_min = MT9T031_COLUMN_SKIP;
+ icd->y_min = MT9T031_ROW_SKIP;
+ icd->x_current = icd->x_min;
+ icd->y_current = icd->y_min;
+ icd->width_min = MT9T031_MIN_WIDTH;
+ icd->width_max = MT9T031_MAX_WIDTH;
+ icd->height_min = MT9T031_MIN_HEIGHT;
+ icd->height_max = MT9T031_MAX_HEIGHT;
+ icd->y_skip_top = 0;
+ icd->iface = icl->bus_id;
+ /* Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width */
+ mt9t031->autoexposure = 1;
+
+ mt9t031->xskip = 1;
+ mt9t031->yskip = 1;
+
+ ret = soc_camera_device_register(icd);
+ if (ret)
+ goto eisdr;
+
+ return 0;
+
+eisdr:
+ i2c_set_clientdata(client, NULL);
+ kfree(mt9t031);
+ return ret;
+}
+
+static int mt9t031_remove(struct i2c_client *client)
+{
+ struct mt9t031 *mt9t031 = i2c_get_clientdata(client);
+
+ soc_camera_device_unregister(&mt9t031->icd);
+ i2c_set_clientdata(client, NULL);
+ kfree(mt9t031);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9t031_id[] = {
+ { "mt9t031", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9t031_id);
+
+static struct i2c_driver mt9t031_i2c_driver = {
+ .driver = {
+ .name = "mt9t031",
+ },
+ .probe = mt9t031_probe,
+ .remove = mt9t031_remove,
+ .id_table = mt9t031_id,
+};
+
+static int __init mt9t031_mod_init(void)
+{
+ return i2c_add_driver(&mt9t031_i2c_driver);
+}
+
+static void __exit mt9t031_mod_exit(void)
+{
+ i2c_del_driver(&mt9t031_i2c_driver);
+}
+
+module_init(mt9t031_mod_init);
+module_exit(mt9t031_mod_exit);
+
+MODULE_DESCRIPTION("Micron MT9T031 Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index 2584201059d8..14a5f9c21ffa 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -273,6 +273,7 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
+ struct soc_camera_link *icl = mt9v022->client->dev.platform_data;
unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK;
int ret;
u16 pixclk = 0;
@@ -296,6 +297,8 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd,
mt9v022->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10;
}
+ flags = soc_camera_apply_sensor_flags(icl, flags);
+
if (flags & SOCAM_PCLK_SAMPLE_RISING)
pixclk |= 0x10;
@@ -337,14 +340,14 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd)
width_flag;
}
-static int mt9v022_set_fmt_cap(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect)
+static int mt9v022_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
{
struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
int ret;
/* The caller provides a supported format, as verified per call to
- * icd->try_fmt_cap(), datawidth is from our supported format list */
+ * icd->try_fmt(), datawidth is from our supported format list */
switch (pixfmt) {
case V4L2_PIX_FMT_GREY:
case V4L2_PIX_FMT_Y16:
@@ -400,18 +403,20 @@ static int mt9v022_set_fmt_cap(struct soc_camera_device *icd,
return 0;
}
-static int mt9v022_try_fmt_cap(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9v022_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
{
- if (f->fmt.pix.height < 32 + icd->y_skip_top)
- f->fmt.pix.height = 32 + icd->y_skip_top;
- if (f->fmt.pix.height > 480 + icd->y_skip_top)
- f->fmt.pix.height = 480 + icd->y_skip_top;
- if (f->fmt.pix.width < 48)
- f->fmt.pix.width = 48;
- if (f->fmt.pix.width > 752)
- f->fmt.pix.width = 752;
- f->fmt.pix.width &= ~0x03; /* ? */
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (pix->height < 32 + icd->y_skip_top)
+ pix->height = 32 + icd->y_skip_top;
+ if (pix->height > 480 + icd->y_skip_top)
+ pix->height = 480 + icd->y_skip_top;
+ if (pix->width < 48)
+ pix->width = 48;
+ if (pix->width > 752)
+ pix->width = 752;
+ pix->width &= ~0x03; /* ? */
return 0;
}
@@ -538,8 +543,8 @@ static struct soc_camera_ops mt9v022_ops = {
.release = mt9v022_release,
.start_capture = mt9v022_start_capture,
.stop_capture = mt9v022_stop_capture,
- .set_fmt_cap = mt9v022_set_fmt_cap,
- .try_fmt_cap = mt9v022_try_fmt_cap,
+ .set_fmt = mt9v022_set_fmt,
+ .try_fmt = mt9v022_try_fmt,
.set_bus_param = mt9v022_set_bus_param,
.query_bus_param = mt9v022_query_bus_param,
.controls = mt9v022_controls,
@@ -690,6 +695,7 @@ static int mt9v022_set_control(struct soc_camera_device *icd,
static int mt9v022_video_probe(struct soc_camera_device *icd)
{
struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
+ struct soc_camera_link *icl = mt9v022->client->dev.platform_data;
s32 data;
int ret;
@@ -725,7 +731,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd)
ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11);
mt9v022->model = V4L2_IDENT_MT9V022IX7ATC;
icd->formats = mt9v022_colour_formats;
- if (mt9v022->client->dev.platform_data)
+ if (gpio_is_valid(icl->gpio))
icd->num_formats = ARRAY_SIZE(mt9v022_colour_formats);
else
icd->num_formats = 1;
@@ -733,7 +739,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd)
ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 0x11);
mt9v022->model = V4L2_IDENT_MT9V022IX7ATM;
icd->formats = mt9v022_monochrome_formats;
- if (mt9v022->client->dev.platform_data)
+ if (gpio_is_valid(icl->gpio))
icd->num_formats = ARRAY_SIZE(mt9v022_monochrome_formats);
else
icd->num_formats = 1;
@@ -760,8 +766,8 @@ static void mt9v022_video_remove(struct soc_camera_device *icd)
struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9v022->client->addr,
- mt9v022->icd.dev.parent, mt9v022->icd.vdev);
- soc_camera_video_stop(&mt9v022->icd);
+ icd->dev.parent, icd->vdev);
+ soc_camera_video_stop(icd);
}
static int mt9v022_probe(struct i2c_client *client,
diff --git a/drivers/media/video/omap24xxcam-dma.c b/drivers/media/video/omap24xxcam-dma.c
new file mode 100644
index 000000000000..1d54b86c936b
--- /dev/null
+++ b/drivers/media/video/omap24xxcam-dma.c
@@ -0,0 +1,601 @@
+/*
+ * drivers/media/video/omap24xxcam-dma.c
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com> and
+ * David Cohen <david.cohen@indt.org.br>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/scatterlist.h>
+
+#include "omap24xxcam.h"
+
+/*
+ *
+ * DMA hardware.
+ *
+ */
+
+/* Ack all interrupt on CSR and IRQSTATUS_L0 */
+static void omap24xxcam_dmahw_ack_all(unsigned long base)
+{
+ u32 csr;
+ int i;
+
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) {
+ csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i));
+ /* ack interrupt in CSR */
+ omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr);
+ }
+ omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf);
+}
+
+/* Ack dmach on CSR and IRQSTATUS_L0 */
+static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach)
+{
+ u32 csr;
+
+ csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach));
+ /* ack interrupt in CSR */
+ omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr);
+ /* ack interrupt in IRQSTATUS */
+ omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach));
+
+ return csr;
+}
+
+static int omap24xxcam_dmahw_running(unsigned long base, int dmach)
+{
+ return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE;
+}
+
+static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach,
+ dma_addr_t start, u32 len)
+{
+ omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_FS
+ | CAMDMA_CCR_WR_ACTIVE
+ | CAMDMA_CCR_RD_ACTIVE
+ | CAMDMA_CCR_SYNCHRO_CAMERA);
+ omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len);
+ omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1);
+ omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach),
+ CAMDMA_CSDP_WRITE_MODE_POSTED
+ | CAMDMA_CSDP_DST_BURST_EN_32
+ | CAMDMA_CSDP_DST_PACKED
+ | CAMDMA_CSDP_SRC_BURST_EN_32
+ | CAMDMA_CSDP_SRC_PACKED
+ | CAMDMA_CSDP_DATA_TYPE_8BITS);
+ omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start);
+ omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD);
+ omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CSR(dmach),
+ CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR
+ | CAMDMA_CSR_BLOCK
+ | CAMDMA_CSR_DROP);
+ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach),
+ CAMDMA_CICR_MISALIGNED_ERR_IE
+ | CAMDMA_CICR_SECURE_ERR_IE
+ | CAMDMA_CICR_TRANS_ERR_IE
+ | CAMDMA_CICR_BLOCK_IE
+ | CAMDMA_CICR_DROP_IE);
+}
+
+static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach)
+{
+ omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_ENABLE
+ | CAMDMA_CCR_FS
+ | CAMDMA_CCR_SYNCHRO_CAMERA);
+}
+
+static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach,
+ int free_dmach)
+{
+ int prev_dmach, ch;
+
+ if (dmach == 0)
+ prev_dmach = NUM_CAMDMA_CHANNELS - 1;
+ else
+ prev_dmach = dmach - 1;
+ omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach),
+ CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach);
+ /* Did we chain the DMA transfer before the previous one
+ * finished?
+ */
+ ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS;
+ while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch))
+ & CAMDMA_CCR_ENABLE)) {
+ if (ch == dmach) {
+ /* The previous transfer has ended and this one
+ * hasn't started, so we must not have chained
+ * to the previous one in time. We'll have to
+ * start it now.
+ */
+ omap24xxcam_dmahw_transfer_start(base, dmach);
+ break;
+ } else
+ ch = (ch + 1) % NUM_CAMDMA_CHANNELS;
+ }
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. The DMA
+ * controller may not be idle after this routine completes, because
+ * the completion routines might start new transfers.
+ */
+static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach)
+{
+ /* mask all interrupts from this channel */
+ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0);
+ /* unlink this channel */
+ omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0,
+ CAMDMA_CLNK_CTRL_ENABLE_LNK);
+ /* disable this channel */
+ omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE);
+}
+
+static void omap24xxcam_dmahw_init(unsigned long base)
+{
+ omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG,
+ CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY
+ | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE
+ | CAMDMA_OCP_SYSCONFIG_AUTOIDLE);
+
+ omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10,
+ CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH);
+
+ omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf);
+}
+
+/*
+ *
+ * Individual DMA channel handling.
+ *
+ */
+
+/* Start a DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully started, or non-zero if all
+ * DMA channels are already in use or starting is currently inhibited.
+ */
+static int omap24xxcam_dma_start(struct omap24xxcam_dma *dma, dma_addr_t start,
+ u32 len, dma_callback_t callback, void *arg)
+{
+ unsigned long flags;
+ int dmach;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ if (!dma->free_dmach || atomic_read(&dma->dma_stop)) {
+ spin_unlock_irqrestore(&dma->lock, flags);
+ return -EBUSY;
+ }
+
+ dmach = dma->next_dmach;
+
+ dma->ch_state[dmach].callback = callback;
+ dma->ch_state[dmach].arg = arg;
+
+ omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len);
+
+ /* We're ready to start the DMA transfer. */
+
+ if (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
+ /* A transfer is already in progress, so try to chain to it. */
+ omap24xxcam_dmahw_transfer_chain(dma->base, dmach,
+ dma->free_dmach);
+ } else {
+ /* No transfer is in progress, so we'll just start this one
+ * now.
+ */
+ omap24xxcam_dmahw_transfer_start(dma->base, dmach);
+ }
+
+ dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS;
+ dma->free_dmach--;
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+
+ return 0;
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. The DMA
+ * controller may not be idle after this routine completes, because
+ * the completion routines might start new transfers.
+ */
+static void omap24xxcam_dma_abort(struct omap24xxcam_dma *dma, u32 csr)
+{
+ unsigned long flags;
+ int dmach, i, free_dmach;
+ dma_callback_t callback;
+ void *arg;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ /* stop any DMA transfers in progress */
+ dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS;
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) {
+ omap24xxcam_dmahw_abort_ch(dma->base, dmach);
+ dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS;
+ }
+
+ /* We have to be careful here because the callback routine
+ * might start a new DMA transfer, and we only want to abort
+ * transfers that were started before this routine was called.
+ */
+ free_dmach = dma->free_dmach;
+ while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) &&
+ (free_dmach < NUM_CAMDMA_CHANNELS)) {
+ dmach = (dma->next_dmach + dma->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ callback = dma->ch_state[dmach].callback;
+ arg = dma->ch_state[dmach].arg;
+ dma->free_dmach++;
+ free_dmach++;
+ if (callback) {
+ /* leave interrupts disabled during callback */
+ spin_unlock(&dma->lock);
+ (*callback) (dma, csr, arg);
+ spin_lock(&dma->lock);
+ }
+ }
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. If the completion
+ * routines attempt to start a new DMA transfer it will fail, so the
+ * DMA controller will be idle after this routine completes.
+ */
+static void omap24xxcam_dma_stop(struct omap24xxcam_dma *dma, u32 csr)
+{
+ atomic_inc(&dma->dma_stop);
+ omap24xxcam_dma_abort(dma, csr);
+ atomic_dec(&dma->dma_stop);
+}
+
+/* Camera DMA interrupt service routine. */
+void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma)
+{
+ int dmach;
+ dma_callback_t callback;
+ void *arg;
+ u32 csr;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&dma->lock);
+
+ if (dma->free_dmach == NUM_CAMDMA_CHANNELS) {
+ /* A camera DMA interrupt occurred while all channels
+ * are idle, so we'll acknowledge the interrupt in the
+ * IRQSTATUS register and exit.
+ */
+ omap24xxcam_dmahw_ack_all(dma->base);
+ spin_unlock(&dma->lock);
+ return;
+ }
+
+ while (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
+ dmach = (dma->next_dmach + dma->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ if (omap24xxcam_dmahw_running(dma->base, dmach)) {
+ /* This buffer hasn't finished yet, so we're done. */
+ break;
+ }
+ csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach);
+ if (csr & csr_error) {
+ /* A DMA error occurred, so stop all DMA
+ * transfers in progress.
+ */
+ spin_unlock(&dma->lock);
+ omap24xxcam_dma_stop(dma, csr);
+ return;
+ } else {
+ callback = dma->ch_state[dmach].callback;
+ arg = dma->ch_state[dmach].arg;
+ dma->free_dmach++;
+ if (callback) {
+ spin_unlock(&dma->lock);
+ (*callback) (dma, csr, arg);
+ spin_lock(&dma->lock);
+ }
+ }
+ }
+
+ spin_unlock(&dma->lock);
+
+ omap24xxcam_sgdma_process(
+ container_of(dma, struct omap24xxcam_sgdma, dma));
+}
+
+void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ omap24xxcam_dmahw_init(dma->base);
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma,
+ unsigned long base)
+{
+ int ch;
+
+ /* group all channels on DMA IRQ0 and unmask irq */
+ spin_lock_init(&dma->lock);
+ dma->base = base;
+ dma->free_dmach = NUM_CAMDMA_CHANNELS;
+ dma->next_dmach = 0;
+ for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) {
+ dma->ch_state[ch].callback = NULL;
+ dma->ch_state[ch].arg = NULL;
+ }
+}
+
+/*
+ *
+ * Scatter-gather DMA.
+ *
+ * High-level DMA construct for transferring whole picture frames to
+ * memory that is discontinuous.
+ *
+ */
+
+/* DMA completion routine for the scatter-gather DMA fragments. */
+static void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr,
+ void *arg)
+{
+ struct omap24xxcam_sgdma *sgdma =
+ container_of(dma, struct omap24xxcam_sgdma, dma);
+ int sgslot = (int)arg;
+ struct sgdma_state *sg_state;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&sgdma->lock);
+
+ /* We got an interrupt, we can remove the timer */
+ del_timer(&sgdma->reset_timer);
+
+ sg_state = sgdma->sg_state + sgslot;
+ if (!sg_state->queued_sglist) {
+ spin_unlock(&sgdma->lock);
+ printk(KERN_ERR "%s: sgdma completed when none queued!\n",
+ __func__);
+ return;
+ }
+
+ sg_state->csr |= csr;
+ if (!--sg_state->queued_sglist) {
+ /* Queue for this sglist is empty, so check to see if we're
+ * done.
+ */
+ if ((sg_state->next_sglist == sg_state->sglen)
+ || (sg_state->csr & csr_error)) {
+ sgdma_callback_t callback = sg_state->callback;
+ void *arg = sg_state->arg;
+ u32 sg_csr = sg_state->csr;
+ /* All done with this sglist */
+ sgdma->free_sgdma++;
+ if (callback) {
+ spin_unlock(&sgdma->lock);
+ (*callback) (sgdma, sg_csr, arg);
+ return;
+ }
+ }
+ }
+
+ spin_unlock(&sgdma->lock);
+}
+
+/* Start queued scatter-gather DMA transfers. */
+void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma)
+{
+ unsigned long flags;
+ int queued_sgdma, sgslot;
+ struct sgdma_state *sg_state;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma;
+ sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
+ while (queued_sgdma > 0) {
+ sg_state = sgdma->sg_state + sgslot;
+ while ((sg_state->next_sglist < sg_state->sglen) &&
+ !(sg_state->csr & csr_error)) {
+ const struct scatterlist *sglist;
+ unsigned int len;
+
+ sglist = sg_state->sglist + sg_state->next_sglist;
+ /* try to start the next DMA transfer */
+ if (sg_state->next_sglist + 1 == sg_state->sglen) {
+ /*
+ * On the last sg, we handle the case where
+ * cam->img.pix.sizeimage % PAGE_ALIGN != 0
+ */
+ len = sg_state->len - sg_state->bytes_read;
+ } else {
+ len = sg_dma_len(sglist);
+ }
+
+ if (omap24xxcam_dma_start(&sgdma->dma,
+ sg_dma_address(sglist),
+ len,
+ omap24xxcam_sgdma_callback,
+ (void *)sgslot)) {
+ /* DMA start failed */
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+ return;
+ } else {
+ unsigned long expires;
+ /* DMA start was successful */
+ sg_state->next_sglist++;
+ sg_state->bytes_read += len;
+ sg_state->queued_sglist++;
+
+ /* We start the reset timer */
+ expires = jiffies + HZ;
+ mod_timer(&sgdma->reset_timer, expires);
+ }
+ }
+ queued_sgdma--;
+ sgslot = (sgslot + 1) % NUM_SG_DMA;
+ }
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+}
+
+/*
+ * Queue a scatter-gather DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully queued, or non-zero
+ * if all of the scatter-gather slots are already in use.
+ */
+int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
+ const struct scatterlist *sglist, int sglen,
+ int len, sgdma_callback_t callback, void *arg)
+{
+ unsigned long flags;
+ struct sgdma_state *sg_state;
+
+ if ((sglen < 0) || ((sglen > 0) & !sglist))
+ return -EINVAL;
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ if (!sgdma->free_sgdma) {
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+ return -EBUSY;
+ }
+
+ sg_state = sgdma->sg_state + sgdma->next_sgdma;
+
+ sg_state->sglist = sglist;
+ sg_state->sglen = sglen;
+ sg_state->next_sglist = 0;
+ sg_state->bytes_read = 0;
+ sg_state->len = len;
+ sg_state->queued_sglist = 0;
+ sg_state->csr = 0;
+ sg_state->callback = callback;
+ sg_state->arg = arg;
+
+ sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA;
+ sgdma->free_sgdma--;
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+
+ omap24xxcam_sgdma_process(sgdma);
+
+ return 0;
+}
+
+/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress.
+ * Any queued scatter-gather DMA transactions that have not yet been started
+ * will remain queued. The DMA controller will be idle after this routine
+ * completes. When the scatter-gather queue is restarted, the next
+ * scatter-gather DMA transfer will begin at the start of a new transaction.
+ */
+void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma)
+{
+ unsigned long flags;
+ int sgslot;
+ struct sgdma_state *sg_state;
+ u32 csr = CAMDMA_CSR_TRANS_ERR;
+
+ /* stop any DMA transfers in progress */
+ omap24xxcam_dma_stop(&sgdma->dma, csr);
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ if (sgdma->free_sgdma < NUM_SG_DMA) {
+ sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
+ sg_state = sgdma->sg_state + sgslot;
+ if (sg_state->next_sglist != 0) {
+ /* This DMA transfer was in progress, so abort it. */
+ sgdma_callback_t callback = sg_state->callback;
+ void *arg = sg_state->arg;
+ sgdma->free_sgdma++;
+ if (callback) {
+ /* leave interrupts masked */
+ spin_unlock(&sgdma->lock);
+ (*callback) (sgdma, csr, arg);
+ spin_lock(&sgdma->lock);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+}
+
+void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
+ unsigned long base,
+ void (*reset_callback)(unsigned long data),
+ unsigned long reset_callback_data)
+{
+ int sg;
+
+ spin_lock_init(&sgdma->lock);
+ sgdma->free_sgdma = NUM_SG_DMA;
+ sgdma->next_sgdma = 0;
+ for (sg = 0; sg < NUM_SG_DMA; sg++) {
+ sgdma->sg_state[sg].sglen = 0;
+ sgdma->sg_state[sg].next_sglist = 0;
+ sgdma->sg_state[sg].bytes_read = 0;
+ sgdma->sg_state[sg].queued_sglist = 0;
+ sgdma->sg_state[sg].csr = 0;
+ sgdma->sg_state[sg].callback = NULL;
+ sgdma->sg_state[sg].arg = NULL;
+ }
+
+ omap24xxcam_dma_init(&sgdma->dma, base);
+ setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data);
+}
diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c
new file mode 100644
index 000000000000..85c3c7c92af1
--- /dev/null
+++ b/drivers/media/video/omap24xxcam.c
@@ -0,0 +1,1908 @@
+/*
+ * drivers/media/video/omap24xxcam.c
+ *
+ * OMAP 2 camera block driver.
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007-2008 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/pci.h> /* needed for videobufs */
+#include <linux/version.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#include "omap24xxcam.h"
+
+#define OMAP24XXCAM_VERSION KERNEL_VERSION(0, 0, 0)
+
+#define RESET_TIMEOUT_NS 10000
+
+static void omap24xxcam_reset(struct omap24xxcam_device *cam);
+static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam);
+static void omap24xxcam_device_unregister(struct v4l2_int_device *s);
+static int omap24xxcam_remove(struct platform_device *pdev);
+
+/* module parameters */
+static int video_nr = -1; /* video device minor (-1 ==> auto assign) */
+/*
+ * Maximum amount of memory to use for capture buffers.
+ * Default is 4800KB, enough to double-buffer SXGA.
+ */
+static int capture_mem = 1280 * 960 * 2 * 2;
+
+static struct v4l2_int_device omap24xxcam;
+
+/*
+ *
+ * Clocks.
+ *
+ */
+
+static void omap24xxcam_clock_put(struct omap24xxcam_device *cam)
+{
+ if (cam->ick != NULL && !IS_ERR(cam->ick))
+ clk_put(cam->ick);
+ if (cam->fck != NULL && !IS_ERR(cam->fck))
+ clk_put(cam->fck);
+
+ cam->ick = cam->fck = NULL;
+}
+
+static int omap24xxcam_clock_get(struct omap24xxcam_device *cam)
+{
+ int rval = 0;
+
+ cam->fck = clk_get(cam->dev, "cam_fck");
+ if (IS_ERR(cam->fck)) {
+ dev_err(cam->dev, "can't get cam_fck");
+ rval = PTR_ERR(cam->fck);
+ omap24xxcam_clock_put(cam);
+ return rval;
+ }
+
+ cam->ick = clk_get(cam->dev, "cam_ick");
+ if (IS_ERR(cam->ick)) {
+ dev_err(cam->dev, "can't get cam_ick");
+ rval = PTR_ERR(cam->ick);
+ omap24xxcam_clock_put(cam);
+ }
+
+ return rval;
+}
+
+static void omap24xxcam_clock_on(struct omap24xxcam_device *cam)
+{
+ clk_enable(cam->fck);
+ clk_enable(cam->ick);
+}
+
+static void omap24xxcam_clock_off(struct omap24xxcam_device *cam)
+{
+ clk_disable(cam->fck);
+ clk_disable(cam->ick);
+}
+
+/*
+ *
+ * Camera core
+ *
+ */
+
+/*
+ * Set xclk.
+ *
+ * To disable xclk, use value zero.
+ */
+static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam,
+ u32 xclk)
+{
+ if (xclk) {
+ u32 divisor = CAM_MCLK / xclk;
+
+ if (divisor == 1)
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK,
+ CC_CTRL_XCLK_DIV_BYPASS);
+ else
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK, divisor);
+ } else
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW);
+}
+
+static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam)
+{
+ /*
+ * Setting the camera core AUTOIDLE bit causes problems with frame
+ * synchronization, so we will clear the AUTOIDLE bit instead.
+ */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG,
+ CC_SYSCONFIG_AUTOIDLE);
+
+ /* program the camera interface DMA packet size */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA,
+ CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1));
+
+ /* enable camera core error interrupts */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE,
+ CC_IRQENABLE_FW_ERR_IRQ
+ | CC_IRQENABLE_FSC_ERR_IRQ
+ | CC_IRQENABLE_SSC_ERR_IRQ
+ | CC_IRQENABLE_FIFO_OF_IRQ);
+}
+
+/*
+ * Enable the camera core.
+ *
+ * Data transfer to the camera DMA starts from next starting frame.
+ */
+static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam)
+{
+
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+ cam->cc_ctrl);
+}
+
+/*
+ * Disable camera core.
+ *
+ * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The
+ * core internal state machines will be reset. Use
+ * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current
+ * frame completely.
+ */
+static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam)
+{
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+ CC_CTRL_CC_RST);
+}
+
+/* Interrupt service routine for camera core interrupts. */
+static void omap24xxcam_core_isr(struct omap24xxcam_device *cam)
+{
+ u32 cc_irqstatus;
+ const u32 cc_irqstatus_err =
+ CC_IRQSTATUS_FW_ERR_IRQ
+ | CC_IRQSTATUS_FSC_ERR_IRQ
+ | CC_IRQSTATUS_SSC_ERR_IRQ
+ | CC_IRQSTATUS_FIFO_UF_IRQ
+ | CC_IRQSTATUS_FIFO_OF_IRQ;
+
+ cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET,
+ CC_IRQSTATUS);
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS,
+ cc_irqstatus);
+
+ if (cc_irqstatus & cc_irqstatus_err
+ && !atomic_read(&cam->in_reset)) {
+ dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n",
+ cc_irqstatus);
+ omap24xxcam_reset(cam);
+ }
+}
+
+/*
+ *
+ * videobuf_buffer handling.
+ *
+ * Memory for mmapped videobuf_buffers is not allocated
+ * conventionally, but by several kmalloc allocations and then
+ * creating the scatterlist on our own. User-space buffers are handled
+ * normally.
+ *
+ */
+
+/*
+ * Free the memory-mapped buffer memory allocated for a
+ * videobuf_buffer and the associated scatterlist.
+ */
+static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+ size_t alloc_size;
+ struct page *page;
+ int i;
+
+ if (dma->sglist == NULL)
+ return;
+
+ i = dma->sglen;
+ while (i) {
+ i--;
+ alloc_size = sg_dma_len(&dma->sglist[i]);
+ page = sg_page(&dma->sglist[i]);
+ do {
+ ClearPageReserved(page++);
+ } while (alloc_size -= PAGE_SIZE);
+ __free_pages(sg_page(&dma->sglist[i]),
+ get_order(sg_dma_len(&dma->sglist[i])));
+ }
+
+ kfree(dma->sglist);
+ dma->sglist = NULL;
+}
+
+/* Release all memory related to the videobuf_queue. */
+static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq)
+{
+ int i;
+
+ mutex_lock(&vbq->vb_lock);
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == vbq->bufs[i])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory)
+ continue;
+ vbq->ops->buf_release(vbq, vbq->bufs[i]);
+ omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+ kfree(vbq->bufs[i]);
+ vbq->bufs[i] = NULL;
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ videobuf_mmap_free(vbq);
+}
+
+/*
+ * Allocate physically as contiguous as possible buffer for video
+ * frame and allocate and build DMA scatter-gather list for it.
+ */
+static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb)
+{
+ unsigned int order;
+ size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */
+ struct page *page;
+ int max_pages, err = 0, i = 0;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ /*
+ * allocate maximum size scatter-gather list. Note this is
+ * overhead. We may not use as many entries as we allocate
+ */
+ max_pages = vb->bsize >> PAGE_SHIFT;
+ dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL);
+ if (dma->sglist == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ while (size) {
+ order = get_order(size);
+ /*
+ * do not over-allocate even if we would get larger
+ * contiguous chunk that way
+ */
+ if ((PAGE_SIZE << order) > size)
+ order--;
+
+ /* try to allocate as many contiguous pages as possible */
+ page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
+ /* if allocation fails, try to allocate smaller amount */
+ while (page == NULL) {
+ order--;
+ page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
+ if (page == NULL && !order) {
+ err = -ENOMEM;
+ goto out;
+ }
+ }
+ size -= (PAGE_SIZE << order);
+
+ /* append allocated chunk of pages into scatter-gather list */
+ sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0);
+ dma->sglen++;
+ i++;
+
+ alloc_size = (PAGE_SIZE << order);
+
+ /* clear pages before giving them to user space */
+ memset(page_address(page), 0, alloc_size);
+
+ /* mark allocated pages reserved */
+ do {
+ SetPageReserved(page++);
+ } while (alloc_size -= PAGE_SIZE);
+ }
+ /*
+ * REVISIT: not fully correct to assign nr_pages == sglen but
+ * video-buf is passing nr_pages for e.g. unmap_sg calls
+ */
+ dma->nr_pages = dma->sglen;
+ dma->direction = PCI_DMA_FROMDEVICE;
+
+ return 0;
+
+out:
+ omap24xxcam_vbq_free_mmap_buffer(vb);
+ return err;
+}
+
+static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq,
+ unsigned int count)
+{
+ int i, err = 0;
+ struct omap24xxcam_fh *fh =
+ container_of(vbq, struct omap24xxcam_fh, vbq);
+
+ mutex_lock(&vbq->vb_lock);
+
+ for (i = 0; i < count; i++) {
+ err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]);
+ if (err)
+ goto out;
+ dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n",
+ videobuf_to_dma(vbq->bufs[i])->sglen, i);
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ return 0;
+out:
+ while (i) {
+ i--;
+ omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ return err;
+}
+
+/*
+ * This routine is called from interrupt context when a scatter-gather DMA
+ * transfer of a videobuf_buffer completes.
+ */
+static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma,
+ u32 csr, void *arg)
+{
+ struct omap24xxcam_device *cam =
+ container_of(sgdma, struct omap24xxcam_device, sgdma);
+ struct omap24xxcam_fh *fh = cam->streaming->private_data;
+ struct videobuf_buffer *vb = (struct videobuf_buffer *)arg;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ if (--cam->sgdma_in_queue == 0)
+ omap24xxcam_core_disable(cam);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+
+ do_gettimeofday(&vb->ts);
+ vb->field_count = atomic_add_return(2, &fh->field_count);
+ if (csr & csr_error) {
+ vb->state = VIDEOBUF_ERROR;
+ if (!atomic_read(&fh->cam->in_reset)) {
+ dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr);
+ omap24xxcam_reset(cam);
+ }
+ } else
+ vb->state = VIDEOBUF_DONE;
+ wake_up(&vb->done);
+}
+
+static void omap24xxcam_vbq_release(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ /* wait for buffer, especially to get out of the sgdma queue */
+ videobuf_waiton(vb, 0, 0);
+ if (vb->memory == V4L2_MEMORY_MMAP) {
+ dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen,
+ dma->direction);
+ dma->direction = DMA_NONE;
+ } else {
+ videobuf_dma_unmap(vbq, videobuf_to_dma(vb));
+ videobuf_dma_free(videobuf_to_dma(vb));
+ }
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+/*
+ * Limit the number of available kernel image capture buffers based on the
+ * number requested, the currently selected image size, and the maximum
+ * amount of memory permitted for kernel capture buffers.
+ */
+static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt,
+ unsigned int *size)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+
+ if (*cnt <= 0)
+ *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
+
+ if (*cnt > VIDEO_MAX_FRAME)
+ *cnt = VIDEO_MAX_FRAME;
+
+ *size = fh->pix.sizeimage;
+
+ /* accessing fh->cam->capture_mem is ok, it's constant */
+ while (*size * *cnt > fh->cam->capture_mem)
+ (*cnt)--;
+
+ return 0;
+}
+
+static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq,
+ struct videobuf_dmabuf *dma)
+{
+ int err = 0;
+
+ dma->direction = PCI_DMA_FROMDEVICE;
+ if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) {
+ kfree(dma->sglist);
+ dma->sglist = NULL;
+ dma->sglen = 0;
+ err = -EIO;
+ }
+
+ return err;
+}
+
+static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+ int err = 0;
+
+ /*
+ * Accessing pix here is okay since it's constant while
+ * streaming is on (and we only get called then).
+ */
+ if (vb->baddr) {
+ /* This is a userspace buffer. */
+ if (fh->pix.sizeimage > vb->bsize) {
+ /* The buffer isn't big enough. */
+ err = -EINVAL;
+ } else
+ vb->size = fh->pix.sizeimage;
+ } else {
+ if (vb->state != VIDEOBUF_NEEDS_INIT) {
+ /*
+ * We have a kernel bounce buffer that has
+ * already been allocated.
+ */
+ if (fh->pix.sizeimage > vb->size) {
+ /*
+ * The image size has been changed to
+ * a larger size since this buffer was
+ * allocated, so we need to free and
+ * reallocate it.
+ */
+ omap24xxcam_vbq_release(vbq, vb);
+ vb->size = fh->pix.sizeimage;
+ }
+ } else {
+ /* We need to allocate a new kernel bounce buffer. */
+ vb->size = fh->pix.sizeimage;
+ }
+ }
+
+ if (err)
+ return err;
+
+ vb->width = fh->pix.width;
+ vb->height = fh->pix.height;
+ vb->field = field;
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ if (vb->memory == V4L2_MEMORY_MMAP)
+ /*
+ * we have built the scatter-gather list by ourself so
+ * do the scatter-gather mapping as well
+ */
+ err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb));
+ else
+ err = videobuf_iolock(vbq, vb, NULL);
+ }
+
+ if (!err)
+ vb->state = VIDEOBUF_PREPARED;
+ else
+ omap24xxcam_vbq_release(vbq, vb);
+
+ return err;
+}
+
+static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ enum videobuf_state state = vb->state;
+ unsigned long flags;
+ int err;
+
+ /*
+ * FIXME: We're marking the buffer active since we have no
+ * pretty way of marking it active exactly when the
+ * scatter-gather transfer starts.
+ */
+ vb->state = VIDEOBUF_ACTIVE;
+
+ err = omap24xxcam_sgdma_queue(&fh->cam->sgdma,
+ videobuf_to_dma(vb)->sglist,
+ videobuf_to_dma(vb)->sglen, vb->size,
+ omap24xxcam_vbq_complete, vb);
+
+ if (!err) {
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ if (++cam->sgdma_in_queue == 1
+ && !atomic_read(&cam->in_reset))
+ omap24xxcam_core_enable(cam);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ } else {
+ /*
+ * Oops. We're not supposed to get any errors here.
+ * The only way we could get an error is if we ran out
+ * of scatter-gather DMA slots, but we are supposed to
+ * have at least as many scatter-gather DMA slots as
+ * video buffers so that can't happen.
+ */
+ dev_err(cam->dev, "failed to queue a video buffer for dma!\n");
+ dev_err(cam->dev, "likely a bug in the driver!\n");
+ vb->state = state;
+ }
+}
+
+static struct videobuf_queue_ops omap24xxcam_vbq_ops = {
+ .buf_setup = omap24xxcam_vbq_setup,
+ .buf_prepare = omap24xxcam_vbq_prepare,
+ .buf_queue = omap24xxcam_vbq_queue,
+ .buf_release = omap24xxcam_vbq_release,
+};
+
+/*
+ *
+ * OMAP main camera system
+ *
+ */
+
+/*
+ * Reset camera block to power-on state.
+ */
+static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam)
+{
+ int max_loop = RESET_TIMEOUT_NS;
+
+ /* Reset whole camera subsystem */
+ omap24xxcam_reg_out(cam->mmio_base,
+ CAM_SYSCONFIG,
+ CAM_SYSCONFIG_SOFTRESET);
+
+ /* Wait till it's finished */
+ while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+ & CAM_SYSSTATUS_RESETDONE)
+ && --max_loop) {
+ ndelay(1);
+ }
+
+ if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+ & CAM_SYSSTATUS_RESETDONE))
+ dev_err(cam->dev, "camera soft reset timeout\n");
+}
+
+/*
+ * (Re)initialise the camera block.
+ */
+static void omap24xxcam_hwinit(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_poweron_reset(cam);
+
+ /* set the camera subsystem autoidle bit */
+ omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG,
+ CAM_SYSCONFIG_AUTOIDLE);
+
+ /* set the camera MMU autoidle bit */
+ omap24xxcam_reg_out(cam->mmio_base,
+ CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG,
+ CAMMMU_SYSCONFIG_AUTOIDLE);
+
+ omap24xxcam_core_hwinit(cam);
+
+ omap24xxcam_dma_hwinit(&cam->sgdma.dma);
+}
+
+/*
+ * Callback for dma transfer stalling.
+ */
+static void omap24xxcam_stalled_dma_reset(unsigned long data)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data;
+
+ if (!atomic_read(&cam->in_reset)) {
+ dev_dbg(cam->dev, "dma stalled, resetting camera\n");
+ omap24xxcam_reset(cam);
+ }
+}
+
+/*
+ * Stop capture. Mark we're doing a reset, stop DMA transfers and
+ * core. (No new scatter-gather transfers will be queued whilst
+ * in_reset is non-zero.)
+ *
+ * If omap24xxcam_capture_stop is called from several places at
+ * once, only the first call will have an effect. Similarly, the last
+ * call omap24xxcam_streaming_cont will have effect.
+ *
+ * Serialisation is ensured by using cam->core_enable_disable_lock.
+ */
+static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+
+ if (atomic_inc_return(&cam->in_reset) != 1) {
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ return;
+ }
+
+ omap24xxcam_core_disable(cam);
+
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+
+ omap24xxcam_sgdma_sync(&cam->sgdma);
+}
+
+/*
+ * Reset and continue streaming.
+ *
+ * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL
+ * register is supposed to be sufficient to recover from a camera
+ * interface error, but it doesn't seem to be enough. If we only do
+ * that then subsequent image captures are out of sync by either one
+ * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the
+ * entire camera subsystem prevents the problem with frame
+ * synchronization.
+ */
+static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+
+ if (atomic_read(&cam->in_reset) != 1)
+ goto out;
+
+ omap24xxcam_hwinit(cam);
+
+ omap24xxcam_sensor_if_enable(cam);
+
+ omap24xxcam_sgdma_process(&cam->sgdma);
+
+ if (cam->sgdma_in_queue)
+ omap24xxcam_core_enable(cam);
+
+out:
+ atomic_dec(&cam->in_reset);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+}
+
+static ssize_t
+omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct omap24xxcam_device *cam = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", cam->streaming ? "active" : "inactive");
+}
+static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL);
+
+/*
+ * Stop capture and restart it. I.e. reset the camera during use.
+ */
+static void omap24xxcam_reset(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_capture_stop(cam);
+ omap24xxcam_capture_cont(cam);
+}
+
+/*
+ * The main interrupt handler.
+ */
+static irqreturn_t omap24xxcam_isr(int irq, void *arg)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg;
+ u32 irqstatus;
+ unsigned int irqhandled = 0;
+
+ irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS);
+
+ if (irqstatus &
+ (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1
+ | CAM_IRQSTATUS_DMA_IRQ0)) {
+ omap24xxcam_dma_isr(&cam->sgdma.dma);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_CC_IRQ) {
+ omap24xxcam_core_isr(cam);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_MMU_IRQ)
+ dev_err(cam->dev, "unhandled camera MMU interrupt!\n");
+
+ return IRQ_RETVAL(irqhandled);
+}
+
+/*
+ *
+ * Sensor handling.
+ *
+ */
+
+/*
+ * Enable the external sensor interface. Try to negotiate interface
+ * parameters with the sensor and start using the new ones. The calls
+ * to sensor_if_enable and sensor_if_disable need not to be balanced.
+ */
+static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam)
+{
+ int rval;
+ struct v4l2_ifparm p;
+
+ rval = vidioc_int_g_ifparm(cam->sdev, &p);
+ if (rval) {
+ dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval);
+ return rval;
+ }
+
+ cam->if_type = p.if_type;
+
+ cam->cc_ctrl = CC_CTRL_CC_EN;
+
+ switch (p.if_type) {
+ case V4L2_IF_TYPE_BT656:
+ if (p.u.bt656.frame_start_on_rising_vs)
+ cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO;
+ if (p.u.bt656.bt_sync_correct)
+ cam->cc_ctrl |= CC_CTRL_BT_CORRECT;
+ if (p.u.bt656.swap)
+ cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM;
+ if (p.u.bt656.latch_clk_inv)
+ cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL;
+ if (p.u.bt656.nobt_hs_inv)
+ cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL;
+ if (p.u.bt656.nobt_vs_inv)
+ cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL;
+
+ switch (p.u.bt656.mode) {
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_BT_8BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_BT_10BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10;
+ break;
+ default:
+ dev_err(cam->dev,
+ "bt656 interface mode %d not supported\n",
+ p.u.bt656.mode);
+ return -EINVAL;
+ }
+ /*
+ * The clock rate that the sensor wants has changed.
+ * We have to adjust the xclk from OMAP 2 side to
+ * match the sensor's wish as closely as possible.
+ */
+ if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) {
+ u32 xclk = p.u.bt656.clock_curr;
+ u32 divisor;
+
+ if (xclk == 0)
+ return -EINVAL;
+
+ if (xclk > CAM_MCLK)
+ xclk = CAM_MCLK;
+
+ divisor = CAM_MCLK / xclk;
+ if (divisor * xclk < CAM_MCLK)
+ divisor++;
+ if (CAM_MCLK / divisor < p.u.bt656.clock_min
+ && divisor > 1)
+ divisor--;
+ if (divisor > 30)
+ divisor = 30;
+
+ xclk = CAM_MCLK / divisor;
+
+ if (xclk < p.u.bt656.clock_min
+ || xclk > p.u.bt656.clock_max)
+ return -EINVAL;
+
+ cam->if_u.bt656.xclk = xclk;
+ }
+ omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk);
+ break;
+ default:
+ /* FIXME: how about other interfaces? */
+ dev_err(cam->dev, "interface type %d not supported\n",
+ p.if_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam)
+{
+ switch (cam->if_type) {
+ case V4L2_IF_TYPE_BT656:
+ omap24xxcam_core_xclk_set(cam, 0);
+ break;
+ }
+}
+
+/*
+ * Initialise the sensor hardware.
+ */
+static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam)
+{
+ int err = 0;
+ struct v4l2_int_device *sdev = cam->sdev;
+
+ omap24xxcam_clock_on(cam);
+ err = omap24xxcam_sensor_if_enable(cam);
+ if (err) {
+ dev_err(cam->dev, "sensor interface could not be enabled at "
+ "initialisation, %d\n", err);
+ cam->sdev = NULL;
+ goto out;
+ }
+
+ /* power up sensor during sensor initialization */
+ vidioc_int_s_power(sdev, 1);
+
+ err = vidioc_int_dev_init(sdev);
+ if (err) {
+ dev_err(cam->dev, "cannot initialize sensor, error %d\n", err);
+ /* Sensor init failed --- it's nonexistent to us! */
+ cam->sdev = NULL;
+ goto out;
+ }
+
+ dev_info(cam->dev, "sensor is %s\n", sdev->name);
+
+out:
+ omap24xxcam_sensor_if_disable(cam);
+ omap24xxcam_clock_off(cam);
+
+ vidioc_int_s_power(sdev, 0);
+
+ return err;
+}
+
+static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam)
+{
+ if (cam->sdev)
+ vidioc_int_dev_exit(cam->sdev);
+}
+
+static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_sensor_if_disable(cam);
+ omap24xxcam_clock_off(cam);
+ vidioc_int_s_power(cam->sdev, 0);
+}
+
+/*
+ * Power-up and configure camera sensor. It's ready for capturing now.
+ */
+static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam)
+{
+ int rval;
+
+ omap24xxcam_clock_on(cam);
+
+ omap24xxcam_sensor_if_enable(cam);
+
+ rval = vidioc_int_s_power(cam->sdev, 1);
+ if (rval)
+ goto out;
+
+ rval = vidioc_int_init(cam->sdev);
+ if (rval)
+ goto out;
+
+ return 0;
+
+out:
+ omap24xxcam_sensor_disable(cam);
+
+ return rval;
+}
+
+static void omap24xxcam_sensor_reset_work(struct work_struct *work)
+{
+ struct omap24xxcam_device *cam =
+ container_of(work, struct omap24xxcam_device,
+ sensor_reset_work);
+
+ if (atomic_read(&cam->reset_disable))
+ return;
+
+ omap24xxcam_capture_stop(cam);
+
+ if (vidioc_int_reset(cam->sdev) == 0) {
+ vidioc_int_init(cam->sdev);
+ } else {
+ /* Can't reset it by vidioc_int_reset. */
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_sensor_enable(cam);
+ }
+
+ omap24xxcam_capture_cont(cam);
+}
+
+/*
+ *
+ * IOCTL interface.
+ *
+ */
+
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+
+ strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
+ cap->version = OMAP24XXCAM_VERSION;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ rval = vidioc_int_enum_fmt_cap(cam->sdev, f);
+
+ return rval;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_fmt_cap(cam->sdev, f);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ rval = vidioc_int_s_fmt_cap(cam->sdev, f);
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ if (!rval) {
+ mutex_lock(&ofh->vbq.vb_lock);
+ ofh->pix = f->fmt.pix;
+ mutex_unlock(&ofh->vbq.vb_lock);
+ }
+
+ memset(f, 0, sizeof(*f));
+ vidioc_g_fmt_vid_cap(file, fh, f);
+
+ return rval;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_try_fmt_cap(cam->sdev, f);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ mutex_unlock(&cam->mutex);
+ return -EBUSY;
+ }
+
+ omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+ mutex_unlock(&cam->mutex);
+
+ rval = videobuf_reqbufs(&ofh->vbq, b);
+
+ /*
+ * Either videobuf_reqbufs failed or the buffers are not
+ * memory-mapped (which would need special attention).
+ */
+ if (rval < 0 || b->memory != V4L2_MEMORY_MMAP)
+ goto out;
+
+ rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval);
+ if (rval)
+ omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+
+out:
+ return rval;
+}
+
+static int vidioc_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+
+ return videobuf_querybuf(&ofh->vbq, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+
+ return videobuf_qbuf(&ofh->vbq, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct videobuf_buffer *vb;
+ int rval;
+
+videobuf_dqbuf_again:
+ rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK);
+ if (rval)
+ goto out;
+
+ vb = ofh->vbq.bufs[b->index];
+
+ mutex_lock(&cam->mutex);
+ /* _needs_reset returns -EIO if reset is required. */
+ rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr);
+ mutex_unlock(&cam->mutex);
+ if (rval == -EIO)
+ schedule_work(&cam->sensor_reset_work);
+ else
+ rval = 0;
+
+out:
+ /*
+ * This is a hack. We don't want to show -EIO to the user
+ * space. Requeue the buffer and try again if we're not doing
+ * this in non-blocking mode.
+ */
+ if (rval == -EIO) {
+ videobuf_qbuf(&ofh->vbq, b);
+ if (!(file->f_flags & O_NONBLOCK))
+ goto videobuf_dqbuf_again;
+ /*
+ * We don't have a videobuf_buffer now --- maybe next
+ * time...
+ */
+ rval = -EAGAIN;
+ }
+
+ return rval;
+}
+
+static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ rval = omap24xxcam_sensor_if_enable(cam);
+ if (rval) {
+ dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n");
+ goto out;
+ }
+
+ rval = videobuf_streamon(&ofh->vbq);
+ if (!rval) {
+ cam->streaming = file;
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ }
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct videobuf_queue *q = &ofh->vbq;
+ int rval;
+
+ atomic_inc(&cam->reset_disable);
+
+ flush_scheduled_work();
+
+ rval = videobuf_streamoff(q);
+ if (!rval) {
+ mutex_lock(&cam->mutex);
+ cam->streaming = NULL;
+ mutex_unlock(&cam->mutex);
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ }
+
+ atomic_dec(&cam->reset_disable);
+
+ return rval;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh,
+ struct v4l2_input *inp)
+{
+ if (inp->index > 0)
+ return -EINVAL;
+
+ strlcpy(inp->name, "camera", sizeof(inp->name));
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ rval = vidioc_int_queryctrl(cam->sdev, a);
+
+ return rval;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_ctrl(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_s_ctrl(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a) {
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_parm(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct v4l2_streamparm old_streamparm;
+ int rval;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rval = vidioc_int_g_parm(cam->sdev, &old_streamparm);
+ if (rval)
+ goto out;
+
+ rval = vidioc_int_s_parm(cam->sdev, a);
+ if (rval)
+ goto out;
+
+ rval = omap24xxcam_sensor_if_enable(cam);
+ /*
+ * Revert to old streaming parameters if enabling sensor
+ * interface with the new ones failed.
+ */
+ if (rval)
+ vidioc_int_s_parm(cam->sdev, &old_streamparm);
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+/*
+ *
+ * File operations.
+ *
+ */
+
+static unsigned int omap24xxcam_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_buffer *vb;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming != file) {
+ mutex_unlock(&cam->mutex);
+ return POLLERR;
+ }
+ mutex_unlock(&cam->mutex);
+
+ mutex_lock(&fh->vbq.vb_lock);
+ if (list_empty(&fh->vbq.stream)) {
+ mutex_unlock(&fh->vbq.vb_lock);
+ return POLLERR;
+ }
+ vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream);
+ mutex_unlock(&fh->vbq.vb_lock);
+
+ poll_wait(file, &vb->done, wait);
+
+ if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int omap24xxcam_mmap_buffers(struct file *file,
+ struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_queue *vbq = &fh->vbq;
+ unsigned int first, last, size, i, j;
+ int err = 0;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ mutex_unlock(&cam->mutex);
+ return -EBUSY;
+ }
+ mutex_unlock(&cam->mutex);
+ mutex_lock(&vbq->vb_lock);
+
+ /* look for first buffer to map */
+ for (first = 0; first < VIDEO_MAX_FRAME; first++) {
+ if (NULL == vbq->bufs[first])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory)
+ continue;
+ if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
+ break;
+ }
+
+ /* look for last buffer to map */
+ for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
+ if (NULL == vbq->bufs[last])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory)
+ continue;
+ size += vbq->bufs[last]->bsize;
+ if (size == (vma->vm_end - vma->vm_start))
+ break;
+ }
+
+ size = 0;
+ for (i = first; i <= last; i++) {
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]);
+
+ for (j = 0; j < dma->sglen; j++) {
+ err = remap_pfn_range(
+ vma, vma->vm_start + size,
+ page_to_pfn(sg_page(&dma->sglist[j])),
+ sg_dma_len(&dma->sglist[j]), vma->vm_page_prot);
+ if (err)
+ goto out;
+ size += sg_dma_len(&dma->sglist[j]);
+ }
+ }
+
+out:
+ mutex_unlock(&vbq->vb_lock);
+
+ return err;
+}
+
+static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ int rval;
+
+ /* let the video-buf mapper check arguments and set-up structures */
+ rval = videobuf_mmap_mapper(&fh->vbq, vma);
+ if (rval)
+ return rval;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* do mapping to our allocated buffers */
+ rval = omap24xxcam_mmap_buffers(file, vma);
+ /*
+ * In case of error, free vma->vm_private_data allocated by
+ * videobuf_mmap_mapper.
+ */
+ if (rval)
+ kfree(vma->vm_private_data);
+
+ return rval;
+}
+
+static int omap24xxcam_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ struct omap24xxcam_device *cam = omap24xxcam.priv;
+ struct omap24xxcam_fh *fh;
+ struct v4l2_format format;
+
+ if (!cam || !cam->vfd || (cam->vfd->minor != minor))
+ return -ENODEV;
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (fh == NULL)
+ return -ENOMEM;
+
+ mutex_lock(&cam->mutex);
+ if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) {
+ mutex_unlock(&cam->mutex);
+ goto out_try_module_get;
+ }
+
+ if (atomic_inc_return(&cam->users) == 1) {
+ omap24xxcam_hwinit(cam);
+ if (omap24xxcam_sensor_enable(cam)) {
+ mutex_unlock(&cam->mutex);
+ goto out_omap24xxcam_sensor_enable;
+ }
+ }
+ mutex_unlock(&cam->mutex);
+
+ fh->cam = cam;
+ mutex_lock(&cam->mutex);
+ vidioc_int_g_fmt_cap(cam->sdev, &format);
+ mutex_unlock(&cam->mutex);
+ /* FIXME: how about fh->pix when there are more users? */
+ fh->pix = format.fmt.pix;
+
+ file->private_data = fh;
+
+ spin_lock_init(&fh->vbq_lock);
+
+ videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL,
+ &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer), fh);
+
+ return 0;
+
+out_omap24xxcam_sensor_enable:
+ omap24xxcam_poweron_reset(cam);
+ module_put(cam->sdev->module);
+
+out_try_module_get:
+ kfree(fh);
+
+ return -ENODEV;
+}
+
+static int omap24xxcam_release(struct inode *inode, struct file *file)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+
+ atomic_inc(&cam->reset_disable);
+
+ flush_scheduled_work();
+
+ /* stop streaming capture */
+ videobuf_streamoff(&fh->vbq);
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming == file) {
+ cam->streaming = NULL;
+ mutex_unlock(&cam->mutex);
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ } else {
+ mutex_unlock(&cam->mutex);
+ }
+
+ atomic_dec(&cam->reset_disable);
+
+ omap24xxcam_vbq_free_mmap_buffers(&fh->vbq);
+
+ /*
+ * Make sure the reset work we might have scheduled is not
+ * pending! It may be run *only* if we have users. (And it may
+ * not be scheduled anymore since streaming is already
+ * disabled.)
+ */
+ flush_scheduled_work();
+
+ mutex_lock(&cam->mutex);
+ if (atomic_dec_return(&cam->users) == 0) {
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_poweron_reset(cam);
+ }
+ mutex_unlock(&cam->mutex);
+
+ file->private_data = NULL;
+
+ module_put(cam->sdev->module);
+ kfree(fh);
+
+ return 0;
+}
+
+static struct file_operations omap24xxcam_fops = {
+ .llseek = no_llseek,
+ .ioctl = video_ioctl2,
+ .poll = omap24xxcam_poll,
+ .mmap = omap24xxcam_mmap,
+ .open = omap24xxcam_open,
+ .release = omap24xxcam_release,
+};
+
+/*
+ *
+ * Power management.
+ *
+ */
+
+#ifdef CONFIG_PM
+static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (atomic_read(&cam->users) == 0)
+ return 0;
+
+ if (!atomic_read(&cam->reset_disable))
+ omap24xxcam_capture_stop(cam);
+
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_poweron_reset(cam);
+
+ return 0;
+}
+
+static int omap24xxcam_resume(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (atomic_read(&cam->users) == 0)
+ return 0;
+
+ omap24xxcam_hwinit(cam);
+ omap24xxcam_sensor_enable(cam);
+
+ if (!atomic_read(&cam->reset_disable))
+ omap24xxcam_capture_cont(cam);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+};
+
+/*
+ *
+ * Camera device (i.e. /dev/video).
+ *
+ */
+
+static int omap24xxcam_device_register(struct v4l2_int_device *s)
+{
+ struct omap24xxcam_device *cam = s->u.slave->master->priv;
+ struct video_device *vfd;
+ int rval;
+
+ /* We already have a slave. */
+ if (cam->sdev)
+ return -EBUSY;
+
+ cam->sdev = s;
+
+ if (device_create_file(cam->dev, &dev_attr_streaming) != 0) {
+ dev_err(cam->dev, "could not register sysfs entry\n");
+ rval = -EBUSY;
+ goto err;
+ }
+
+ /* initialize the video_device struct */
+ vfd = cam->vfd = video_device_alloc();
+ if (!vfd) {
+ dev_err(cam->dev, "could not allocate video device struct\n");
+ rval = -ENOMEM;
+ goto err;
+ }
+ vfd->release = video_device_release;
+
+ vfd->parent = cam->dev;
+
+ strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
+ vfd->vfl_type = VID_TYPE_CAPTURE | VID_TYPE_CHROMAKEY;
+ vfd->fops = &omap24xxcam_fops;
+ vfd->minor = -1;
+ vfd->ioctl_ops = &omap24xxcam_ioctl_fops;
+
+ omap24xxcam_hwinit(cam);
+
+ rval = omap24xxcam_sensor_init(cam);
+ if (rval)
+ goto err;
+
+ if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
+ dev_err(cam->dev, "could not register V4L device\n");
+ vfd->minor = -1;
+ rval = -EBUSY;
+ goto err;
+ }
+
+ omap24xxcam_poweron_reset(cam);
+
+ dev_info(cam->dev, "registered device video%d\n", vfd->minor);
+
+ return 0;
+
+err:
+ omap24xxcam_device_unregister(s);
+
+ return rval;
+}
+
+static void omap24xxcam_device_unregister(struct v4l2_int_device *s)
+{
+ struct omap24xxcam_device *cam = s->u.slave->master->priv;
+
+ omap24xxcam_sensor_exit(cam);
+
+ if (cam->vfd) {
+ if (cam->vfd->minor == -1) {
+ /*
+ * The device was never registered, so release the
+ * video_device struct directly.
+ */
+ video_device_release(cam->vfd);
+ } else {
+ /*
+ * The unregister function will release the
+ * video_device struct as well as
+ * unregistering it.
+ */
+ video_unregister_device(cam->vfd);
+ }
+ cam->vfd = NULL;
+ }
+
+ device_remove_file(cam->dev, &dev_attr_streaming);
+
+ cam->sdev = NULL;
+}
+
+static struct v4l2_int_master omap24xxcam_master = {
+ .attach = omap24xxcam_device_register,
+ .detach = omap24xxcam_device_unregister,
+};
+
+static struct v4l2_int_device omap24xxcam = {
+ .module = THIS_MODULE,
+ .name = CAM_NAME,
+ .type = v4l2_int_type_master,
+ .u = {
+ .master = &omap24xxcam_master
+ },
+};
+
+/*
+ *
+ * Driver initialisation and deinitialisation.
+ *
+ */
+
+static int __init omap24xxcam_probe(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam;
+ struct resource *mem;
+ int irq;
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam) {
+ dev_err(&pdev->dev, "could not allocate memory\n");
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, cam);
+
+ cam->dev = &pdev->dev;
+
+ /*
+ * Impose a lower limit on the amount of memory allocated for
+ * capture. We require at least enough memory to double-buffer
+ * QVGA (300KB).
+ */
+ if (capture_mem < 320 * 240 * 2 * 2)
+ capture_mem = 320 * 240 * 2 * 2;
+ cam->capture_mem = capture_mem;
+
+ /* request the mem region for the camera registers */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(cam->dev, "no mem resource?\n");
+ goto err;
+ }
+ if (!request_mem_region(mem->start, (mem->end - mem->start) + 1,
+ pdev->name)) {
+ dev_err(cam->dev,
+ "cannot reserve camera register I/O region\n");
+ goto err;
+ }
+ cam->mmio_base_phys = mem->start;
+ cam->mmio_size = (mem->end - mem->start) + 1;
+
+ /* map the region */
+ cam->mmio_base = (unsigned long)
+ ioremap_nocache(cam->mmio_base_phys, cam->mmio_size);
+ if (!cam->mmio_base) {
+ dev_err(cam->dev, "cannot map camera register I/O region\n");
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(cam->dev, "no irq for camera?\n");
+ goto err;
+ }
+
+ /* install the interrupt service routine */
+ if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) {
+ dev_err(cam->dev,
+ "could not install interrupt service routine\n");
+ goto err;
+ }
+ cam->irq = irq;
+
+ if (omap24xxcam_clock_get(cam))
+ goto err;
+
+ INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work);
+
+ mutex_init(&cam->mutex);
+ spin_lock_init(&cam->core_enable_disable_lock);
+
+ omap24xxcam_sgdma_init(&cam->sgdma,
+ cam->mmio_base + CAMDMA_REG_OFFSET,
+ omap24xxcam_stalled_dma_reset,
+ (unsigned long)cam);
+
+ omap24xxcam.priv = cam;
+
+ if (v4l2_int_device_register(&omap24xxcam))
+ goto err;
+
+ return 0;
+
+err:
+ omap24xxcam_remove(pdev);
+ return -ENODEV;
+}
+
+static int omap24xxcam_remove(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (!cam)
+ return 0;
+
+ if (omap24xxcam.priv != NULL)
+ v4l2_int_device_unregister(&omap24xxcam);
+ omap24xxcam.priv = NULL;
+
+ omap24xxcam_clock_put(cam);
+
+ if (cam->irq) {
+ free_irq(cam->irq, cam);
+ cam->irq = 0;
+ }
+
+ if (cam->mmio_base) {
+ iounmap((void *)cam->mmio_base);
+ cam->mmio_base = 0;
+ }
+
+ if (cam->mmio_base_phys) {
+ release_mem_region(cam->mmio_base_phys, cam->mmio_size);
+ cam->mmio_base_phys = 0;
+ }
+
+ kfree(cam);
+
+ return 0;
+}
+
+static struct platform_driver omap24xxcam_driver = {
+ .probe = omap24xxcam_probe,
+ .remove = omap24xxcam_remove,
+#ifdef CONFIG_PM
+ .suspend = omap24xxcam_suspend,
+ .resume = omap24xxcam_resume,
+#endif
+ .driver = {
+ .name = CAM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*
+ *
+ * Module initialisation and deinitialisation
+ *
+ */
+
+static int __init omap24xxcam_init(void)
+{
+ return platform_driver_register(&omap24xxcam_driver);
+}
+
+static void __exit omap24xxcam_cleanup(void)
+{
+ platform_driver_unregister(&omap24xxcam_driver);
+}
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver");
+MODULE_LICENSE("GPL");
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr,
+ "Minor number for video device (-1 ==> auto assign)");
+module_param(capture_mem, int, 0);
+MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture "
+ "buffers (default 4800kiB)");
+
+module_init(omap24xxcam_init);
+module_exit(omap24xxcam_cleanup);
diff --git a/drivers/media/video/omap24xxcam.h b/drivers/media/video/omap24xxcam.h
new file mode 100644
index 000000000000..2ce67f5a48d5
--- /dev/null
+++ b/drivers/media/video/omap24xxcam.h
@@ -0,0 +1,593 @@
+/*
+ * drivers/media/video/omap24xxcam.h
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP24XXCAM_H
+#define OMAP24XXCAM_H
+
+#include <media/videobuf-dma-sg.h>
+#include <media/v4l2-int-device.h>
+
+/*
+ *
+ * General driver related definitions.
+ *
+ */
+
+#define CAM_NAME "omap24xxcam"
+
+#define CAM_MCLK 96000000
+
+/* number of bytes transferred per DMA request */
+#define DMA_THRESHOLD 32
+
+/*
+ * NUM_CAMDMA_CHANNELS is the number of logical channels provided by
+ * the camera DMA controller.
+ */
+#define NUM_CAMDMA_CHANNELS 4
+
+/*
+ * NUM_SG_DMA is the number of scatter-gather DMA transfers that can
+ * be queued. (We don't have any overlay sglists now.)
+ */
+#define NUM_SG_DMA (VIDEO_MAX_FRAME)
+
+/*
+ *
+ * Register definitions.
+ *
+ */
+
+/* subsystem register block offsets */
+#define CC_REG_OFFSET 0x00000400
+#define CAMDMA_REG_OFFSET 0x00000800
+#define CAMMMU_REG_OFFSET 0x00000C00
+
+/* define camera subsystem register offsets */
+#define CAM_REVISION 0x000
+#define CAM_SYSCONFIG 0x010
+#define CAM_SYSSTATUS 0x014
+#define CAM_IRQSTATUS 0x018
+#define CAM_GPO 0x040
+#define CAM_GPI 0x050
+
+/* define camera core register offsets */
+#define CC_REVISION 0x000
+#define CC_SYSCONFIG 0x010
+#define CC_SYSSTATUS 0x014
+#define CC_IRQSTATUS 0x018
+#define CC_IRQENABLE 0x01C
+#define CC_CTRL 0x040
+#define CC_CTRL_DMA 0x044
+#define CC_CTRL_XCLK 0x048
+#define CC_FIFODATA 0x04C
+#define CC_TEST 0x050
+#define CC_GENPAR 0x054
+#define CC_CCPFSCR 0x058
+#define CC_CCPFECR 0x05C
+#define CC_CCPLSCR 0x060
+#define CC_CCPLECR 0x064
+#define CC_CCPDFR 0x068
+
+/* define camera dma register offsets */
+#define CAMDMA_REVISION 0x000
+#define CAMDMA_IRQSTATUS_L0 0x008
+#define CAMDMA_IRQSTATUS_L1 0x00C
+#define CAMDMA_IRQSTATUS_L2 0x010
+#define CAMDMA_IRQSTATUS_L3 0x014
+#define CAMDMA_IRQENABLE_L0 0x018
+#define CAMDMA_IRQENABLE_L1 0x01C
+#define CAMDMA_IRQENABLE_L2 0x020
+#define CAMDMA_IRQENABLE_L3 0x024
+#define CAMDMA_SYSSTATUS 0x028
+#define CAMDMA_OCP_SYSCONFIG 0x02C
+#define CAMDMA_CAPS_0 0x064
+#define CAMDMA_CAPS_2 0x06C
+#define CAMDMA_CAPS_3 0x070
+#define CAMDMA_CAPS_4 0x074
+#define CAMDMA_GCR 0x078
+#define CAMDMA_CCR(n) (0x080 + (n)*0x60)
+#define CAMDMA_CLNK_CTRL(n) (0x084 + (n)*0x60)
+#define CAMDMA_CICR(n) (0x088 + (n)*0x60)
+#define CAMDMA_CSR(n) (0x08C + (n)*0x60)
+#define CAMDMA_CSDP(n) (0x090 + (n)*0x60)
+#define CAMDMA_CEN(n) (0x094 + (n)*0x60)
+#define CAMDMA_CFN(n) (0x098 + (n)*0x60)
+#define CAMDMA_CSSA(n) (0x09C + (n)*0x60)
+#define CAMDMA_CDSA(n) (0x0A0 + (n)*0x60)
+#define CAMDMA_CSEI(n) (0x0A4 + (n)*0x60)
+#define CAMDMA_CSFI(n) (0x0A8 + (n)*0x60)
+#define CAMDMA_CDEI(n) (0x0AC + (n)*0x60)
+#define CAMDMA_CDFI(n) (0x0B0 + (n)*0x60)
+#define CAMDMA_CSAC(n) (0x0B4 + (n)*0x60)
+#define CAMDMA_CDAC(n) (0x0B8 + (n)*0x60)
+#define CAMDMA_CCEN(n) (0x0BC + (n)*0x60)
+#define CAMDMA_CCFN(n) (0x0C0 + (n)*0x60)
+#define CAMDMA_COLOR(n) (0x0C4 + (n)*0x60)
+
+/* define camera mmu register offsets */
+#define CAMMMU_REVISION 0x000
+#define CAMMMU_SYSCONFIG 0x010
+#define CAMMMU_SYSSTATUS 0x014
+#define CAMMMU_IRQSTATUS 0x018
+#define CAMMMU_IRQENABLE 0x01C
+#define CAMMMU_WALKING_ST 0x040
+#define CAMMMU_CNTL 0x044
+#define CAMMMU_FAULT_AD 0x048
+#define CAMMMU_TTB 0x04C
+#define CAMMMU_LOCK 0x050
+#define CAMMMU_LD_TLB 0x054
+#define CAMMMU_CAM 0x058
+#define CAMMMU_RAM 0x05C
+#define CAMMMU_GFLUSH 0x060
+#define CAMMMU_FLUSH_ENTRY 0x064
+#define CAMMMU_READ_CAM 0x068
+#define CAMMMU_READ_RAM 0x06C
+#define CAMMMU_EMU_FAULT_AD 0x070
+
+/* Define bit fields within selected registers */
+#define CAM_REVISION_MAJOR (15 << 4)
+#define CAM_REVISION_MAJOR_SHIFT 4
+#define CAM_REVISION_MINOR (15 << 0)
+#define CAM_REVISION_MINOR_SHIFT 0
+
+#define CAM_SYSCONFIG_SOFTRESET (1 << 1)
+#define CAM_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CAM_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CAM_IRQSTATUS_CC_IRQ (1 << 4)
+#define CAM_IRQSTATUS_MMU_IRQ (1 << 3)
+#define CAM_IRQSTATUS_DMA_IRQ2 (1 << 2)
+#define CAM_IRQSTATUS_DMA_IRQ1 (1 << 1)
+#define CAM_IRQSTATUS_DMA_IRQ0 (1 << 0)
+
+#define CAM_GPO_CAM_S_P_EN (1 << 1)
+#define CAM_GPO_CAM_CCP_MODE (1 << 0)
+
+#define CAM_GPI_CC_DMA_REQ1 (1 << 24)
+#define CAP_GPI_CC_DMA_REQ0 (1 << 23)
+#define CAP_GPI_CAM_MSTANDBY (1 << 21)
+#define CAP_GPI_CAM_WAIT (1 << 20)
+#define CAP_GPI_CAM_S_DATA (1 << 17)
+#define CAP_GPI_CAM_S_CLK (1 << 16)
+#define CAP_GPI_CAM_P_DATA (0xFFF << 3)
+#define CAP_GPI_CAM_P_DATA_SHIFT 3
+#define CAP_GPI_CAM_P_VS (1 << 2)
+#define CAP_GPI_CAM_P_HS (1 << 1)
+#define CAP_GPI_CAM_P_CLK (1 << 0)
+
+#define CC_REVISION_MAJOR (15 << 4)
+#define CC_REVISION_MAJOR_SHIFT 4
+#define CC_REVISION_MINOR (15 << 0)
+#define CC_REVISION_MINOR_SHIFT 0
+
+#define CC_SYSCONFIG_SIDLEMODE (3 << 3)
+#define CC_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
+#define CC_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
+#define CC_SYSCONFIG_SOFTRESET (1 << 1)
+#define CC_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CC_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CC_IRQSTATUS_FS_IRQ (1 << 19)
+#define CC_IRQSTATUS_LE_IRQ (1 << 18)
+#define CC_IRQSTATUS_LS_IRQ (1 << 17)
+#define CC_IRQSTATUS_FE_IRQ (1 << 16)
+#define CC_IRQSTATUS_FW_ERR_IRQ (1 << 10)
+#define CC_IRQSTATUS_FSC_ERR_IRQ (1 << 9)
+#define CC_IRQSTATUS_SSC_ERR_IRQ (1 << 8)
+#define CC_IRQSTATUS_FIFO_NOEMPTY_IRQ (1 << 4)
+#define CC_IRQSTATUS_FIFO_FULL_IRQ (1 << 3)
+#define CC_IRQSTATUS_FIFO_THR_IRQ (1 << 2)
+#define CC_IRQSTATUS_FIFO_OF_IRQ (1 << 1)
+#define CC_IRQSTATUS_FIFO_UF_IRQ (1 << 0)
+
+#define CC_IRQENABLE_FS_IRQ (1 << 19)
+#define CC_IRQENABLE_LE_IRQ (1 << 18)
+#define CC_IRQENABLE_LS_IRQ (1 << 17)
+#define CC_IRQENABLE_FE_IRQ (1 << 16)
+#define CC_IRQENABLE_FW_ERR_IRQ (1 << 10)
+#define CC_IRQENABLE_FSC_ERR_IRQ (1 << 9)
+#define CC_IRQENABLE_SSC_ERR_IRQ (1 << 8)
+#define CC_IRQENABLE_FIFO_NOEMPTY_IRQ (1 << 4)
+#define CC_IRQENABLE_FIFO_FULL_IRQ (1 << 3)
+#define CC_IRQENABLE_FIFO_THR_IRQ (1 << 2)
+#define CC_IRQENABLE_FIFO_OF_IRQ (1 << 1)
+#define CC_IRQENABLE_FIFO_UF_IRQ (1 << 0)
+
+#define CC_CTRL_CC_ONE_SHOT (1 << 20)
+#define CC_CTRL_CC_IF_SYNCHRO (1 << 19)
+#define CC_CTRL_CC_RST (1 << 18)
+#define CC_CTRL_CC_FRAME_TRIG (1 << 17)
+#define CC_CTRL_CC_EN (1 << 16)
+#define CC_CTRL_NOBT_SYNCHRO (1 << 13)
+#define CC_CTRL_BT_CORRECT (1 << 12)
+#define CC_CTRL_PAR_ORDERCAM (1 << 11)
+#define CC_CTRL_PAR_CLK_POL (1 << 10)
+#define CC_CTRL_NOBT_HS_POL (1 << 9)
+#define CC_CTRL_NOBT_VS_POL (1 << 8)
+#define CC_CTRL_PAR_MODE (7 << 1)
+#define CC_CTRL_PAR_MODE_SHIFT 1
+#define CC_CTRL_PAR_MODE_NOBT8 (0 << 1)
+#define CC_CTRL_PAR_MODE_NOBT10 (1 << 1)
+#define CC_CTRL_PAR_MODE_NOBT12 (2 << 1)
+#define CC_CTRL_PAR_MODE_BT8 (4 << 1)
+#define CC_CTRL_PAR_MODE_BT10 (5 << 1)
+#define CC_CTRL_PAR_MODE_FIFOTEST (7 << 1)
+#define CC_CTRL_CCP_MODE (1 << 0)
+
+#define CC_CTRL_DMA_EN (1 << 8)
+#define CC_CTRL_DMA_FIFO_THRESHOLD (0x7F << 0)
+#define CC_CTRL_DMA_FIFO_THRESHOLD_SHIFT 0
+
+#define CC_CTRL_XCLK_DIV (0x1F << 0)
+#define CC_CTRL_XCLK_DIV_SHIFT 0
+#define CC_CTRL_XCLK_DIV_STABLE_LOW (0 << 0)
+#define CC_CTRL_XCLK_DIV_STABLE_HIGH (1 << 0)
+#define CC_CTRL_XCLK_DIV_BYPASS (31 << 0)
+
+#define CC_TEST_FIFO_RD_POINTER (0xFF << 24)
+#define CC_TEST_FIFO_RD_POINTER_SHIFT 24
+#define CC_TEST_FIFO_WR_POINTER (0xFF << 16)
+#define CC_TEST_FIFO_WR_POINTER_SHIFT 16
+#define CC_TEST_FIFO_LEVEL (0xFF << 8)
+#define CC_TEST_FIFO_LEVEL_SHIFT 8
+#define CC_TEST_FIFO_LEVEL_PEAK (0xFF << 0)
+#define CC_TEST_FIFO_LEVEL_PEAK_SHIFT 0
+
+#define CC_GENPAR_FIFO_DEPTH (7 << 0)
+#define CC_GENPAR_FIFO_DEPTH_SHIFT 0
+
+#define CC_CCPDFR_ALPHA (0xFF << 8)
+#define CC_CCPDFR_ALPHA_SHIFT 8
+#define CC_CCPDFR_DATAFORMAT (15 << 0)
+#define CC_CCPDFR_DATAFORMAT_SHIFT 0
+#define CC_CCPDFR_DATAFORMAT_YUV422BE (0 << 0)
+#define CC_CCPDFR_DATAFORMAT_YUV422 (1 << 0)
+#define CC_CCPDFR_DATAFORMAT_YUV420 (2 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB444 (4 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB565 (5 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB888NDE (6 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB888 (7 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW8NDE (8 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW8 (9 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW10NDE (10 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW10 (11 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW12NDE (12 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW12 (13 << 0)
+#define CC_CCPDFR_DATAFORMAT_JPEG8 (15 << 0)
+
+#define CAMDMA_REVISION_MAJOR (15 << 4)
+#define CAMDMA_REVISION_MAJOR_SHIFT 4
+#define CAMDMA_REVISION_MINOR (15 << 0)
+#define CAMDMA_REVISION_MINOR_SHIFT 0
+
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE (3 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY (0 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_NSTANDBY (1 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_SSTANDBY (2 << 12)
+#define CAMDMA_OCP_SYSCONFIG_FUNC_CLOCK (1 << 9)
+#define CAMDMA_OCP_SYSCONFIG_OCP_CLOCK (1 << 8)
+#define CAMDMA_OCP_SYSCONFIG_EMUFREE (1 << 5)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE (3 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_SIDLE (2 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SOFTRESET (1 << 1)
+#define CAMDMA_OCP_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CAMDMA_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CAMDMA_GCR_ARBITRATION_RATE (0xFF << 16)
+#define CAMDMA_GCR_ARBITRATION_RATE_SHIFT 16
+#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH (0xFF << 0)
+#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH_SHIFT 0
+
+#define CAMDMA_CCR_SEL_SRC_DST_SYNC (1 << 24)
+#define CAMDMA_CCR_PREFETCH (1 << 23)
+#define CAMDMA_CCR_SUPERVISOR (1 << 22)
+#define CAMDMA_CCR_SECURE (1 << 21)
+#define CAMDMA_CCR_BS (1 << 18)
+#define CAMDMA_CCR_TRANSPARENT_COPY_ENABLE (1 << 17)
+#define CAMDMA_CCR_CONSTANT_FILL_ENABLE (1 << 16)
+#define CAMDMA_CCR_DST_AMODE (3 << 14)
+#define CAMDMA_CCR_DST_AMODE_CONST_ADDR (0 << 14)
+#define CAMDMA_CCR_DST_AMODE_POST_INC (1 << 14)
+#define CAMDMA_CCR_DST_AMODE_SGL_IDX (2 << 14)
+#define CAMDMA_CCR_DST_AMODE_DBL_IDX (3 << 14)
+#define CAMDMA_CCR_SRC_AMODE (3 << 12)
+#define CAMDMA_CCR_SRC_AMODE_CONST_ADDR (0 << 12)
+#define CAMDMA_CCR_SRC_AMODE_POST_INC (1 << 12)
+#define CAMDMA_CCR_SRC_AMODE_SGL_IDX (2 << 12)
+#define CAMDMA_CCR_SRC_AMODE_DBL_IDX (3 << 12)
+#define CAMDMA_CCR_WR_ACTIVE (1 << 10)
+#define CAMDMA_CCR_RD_ACTIVE (1 << 9)
+#define CAMDMA_CCR_SUSPEND_SENSITIVE (1 << 8)
+#define CAMDMA_CCR_ENABLE (1 << 7)
+#define CAMDMA_CCR_PRIO (1 << 6)
+#define CAMDMA_CCR_FS (1 << 5)
+#define CAMDMA_CCR_SYNCHRO ((3 << 19) | (31 << 0))
+#define CAMDMA_CCR_SYNCHRO_CAMERA 0x01
+
+#define CAMDMA_CLNK_CTRL_ENABLE_LNK (1 << 15)
+#define CAMDMA_CLNK_CTRL_NEXTLCH_ID (0x1F << 0)
+#define CAMDMA_CLNK_CTRL_NEXTLCH_ID_SHIFT 0
+
+#define CAMDMA_CICR_MISALIGNED_ERR_IE (1 << 11)
+#define CAMDMA_CICR_SUPERVISOR_ERR_IE (1 << 10)
+#define CAMDMA_CICR_SECURE_ERR_IE (1 << 9)
+#define CAMDMA_CICR_TRANS_ERR_IE (1 << 8)
+#define CAMDMA_CICR_PACKET_IE (1 << 7)
+#define CAMDMA_CICR_BLOCK_IE (1 << 5)
+#define CAMDMA_CICR_LAST_IE (1 << 4)
+#define CAMDMA_CICR_FRAME_IE (1 << 3)
+#define CAMDMA_CICR_HALF_IE (1 << 2)
+#define CAMDMA_CICR_DROP_IE (1 << 1)
+
+#define CAMDMA_CSR_MISALIGNED_ERR (1 << 11)
+#define CAMDMA_CSR_SUPERVISOR_ERR (1 << 10)
+#define CAMDMA_CSR_SECURE_ERR (1 << 9)
+#define CAMDMA_CSR_TRANS_ERR (1 << 8)
+#define CAMDMA_CSR_PACKET (1 << 7)
+#define CAMDMA_CSR_SYNC (1 << 6)
+#define CAMDMA_CSR_BLOCK (1 << 5)
+#define CAMDMA_CSR_LAST (1 << 4)
+#define CAMDMA_CSR_FRAME (1 << 3)
+#define CAMDMA_CSR_HALF (1 << 2)
+#define CAMDMA_CSR_DROP (1 << 1)
+
+#define CAMDMA_CSDP_SRC_ENDIANNESS (1 << 21)
+#define CAMDMA_CSDP_SRC_ENDIANNESS_LOCK (1 << 20)
+#define CAMDMA_CSDP_DST_ENDIANNESS (1 << 19)
+#define CAMDMA_CSDP_DST_ENDIANNESS_LOCK (1 << 18)
+#define CAMDMA_CSDP_WRITE_MODE (3 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_WRNP (0 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_POSTED (1 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_POSTED_LAST_WRNP (2 << 16)
+#define CAMDMA_CSDP_DST_BURST_EN (3 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_1 (0 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_16 (1 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_32 (2 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_64 (3 << 14)
+#define CAMDMA_CSDP_DST_PACKED (1 << 13)
+#define CAMDMA_CSDP_WR_ADD_TRSLT (15 << 9)
+#define CAMDMA_CSDP_WR_ADD_TRSLT_ENABLE_MREQADD (3 << 9)
+#define CAMDMA_CSDP_SRC_BURST_EN (3 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_1 (0 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_16 (1 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_32 (2 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_64 (3 << 7)
+#define CAMDMA_CSDP_SRC_PACKED (1 << 6)
+#define CAMDMA_CSDP_RD_ADD_TRSLT (15 << 2)
+#define CAMDMA_CSDP_RD_ADD_TRSLT_ENABLE_MREQADD (3 << 2)
+#define CAMDMA_CSDP_DATA_TYPE (3 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_8BITS (0 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_16BITS (1 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_32BITS (2 << 0)
+
+#define CAMMMU_SYSCONFIG_AUTOIDLE (1 << 0)
+
+/*
+ *
+ * Declarations.
+ *
+ */
+
+/* forward declarations */
+struct omap24xxcam_sgdma;
+struct omap24xxcam_dma;
+
+typedef void (*sgdma_callback_t)(struct omap24xxcam_sgdma *cam,
+ u32 status, void *arg);
+typedef void (*dma_callback_t)(struct omap24xxcam_dma *cam,
+ u32 status, void *arg);
+
+struct channel_state {
+ dma_callback_t callback;
+ void *arg;
+};
+
+/* sgdma state for each of the possible videobuf_buffers + 2 overlays */
+struct sgdma_state {
+ const struct scatterlist *sglist;
+ int sglen; /* number of sglist entries */
+ int next_sglist; /* index of next sglist entry to process */
+ unsigned int bytes_read; /* number of bytes read */
+ unsigned int len; /* total length of sglist (excluding
+ * bytes due to page alignment) */
+ int queued_sglist; /* number of sglist entries queued for DMA */
+ u32 csr; /* DMA return code */
+ sgdma_callback_t callback;
+ void *arg;
+};
+
+/* physical DMA channel management */
+struct omap24xxcam_dma {
+ spinlock_t lock; /* Lock for the whole structure. */
+
+ unsigned long base; /* base address for dma controller */
+
+ /* While dma_stop!=0, an attempt to start a new DMA transfer will
+ * fail.
+ */
+ atomic_t dma_stop;
+ int free_dmach; /* number of dma channels free */
+ int next_dmach; /* index of next dma channel to use */
+ struct channel_state ch_state[NUM_CAMDMA_CHANNELS];
+};
+
+/* scatter-gather DMA (scatterlist stuff) management */
+struct omap24xxcam_sgdma {
+ struct omap24xxcam_dma dma;
+
+ spinlock_t lock; /* Lock for the fields below. */
+ int free_sgdma; /* number of free sg dma slots */
+ int next_sgdma; /* index of next sg dma slot to use */
+ struct sgdma_state sg_state[NUM_SG_DMA];
+
+ /* Reset timer data */
+ struct timer_list reset_timer;
+};
+
+/* per-device data structure */
+struct omap24xxcam_device {
+ /*** mutex ***/
+ /*
+ * mutex serialises access to this structure. Also camera
+ * opening and releasing is synchronised by this.
+ */
+ struct mutex mutex;
+
+ /*** general driver state information ***/
+ atomic_t users;
+ /*
+ * Lock to serialise core enabling and disabling and access to
+ * sgdma_in_queue.
+ */
+ spinlock_t core_enable_disable_lock;
+ /*
+ * Number or sgdma requests in scatter-gather queue, protected
+ * by the lock above.
+ */
+ int sgdma_in_queue;
+ /*
+ * Sensor interface parameters: interface type, CC_CTRL
+ * register value and interface specific data.
+ */
+ int if_type;
+ union {
+ struct parallel {
+ u32 xclk;
+ } bt656;
+ } if_u;
+ u32 cc_ctrl;
+
+ /*** subsystem structures ***/
+ struct omap24xxcam_sgdma sgdma;
+
+ /*** hardware resources ***/
+ unsigned int irq;
+ unsigned long mmio_base;
+ unsigned long mmio_base_phys;
+ unsigned long mmio_size;
+
+ /*** interfaces and device ***/
+ struct v4l2_int_device *sdev;
+ struct device *dev;
+ struct video_device *vfd;
+
+ /*** camera and sensor reset related stuff ***/
+ struct work_struct sensor_reset_work;
+ /*
+ * We're in the middle of a reset. Don't enable core if this
+ * is non-zero! This exists to help decisionmaking in a case
+ * where videobuf_qbuf is called while we are in the middle of
+ * a reset.
+ */
+ atomic_t in_reset;
+ /*
+ * Non-zero if we don't want any resets for now. Used to
+ * prevent reset work to run when we're about to stop
+ * streaming.
+ */
+ atomic_t reset_disable;
+
+ /*** video device parameters ***/
+ int capture_mem;
+
+ /*** camera module clocks ***/
+ struct clk *fck;
+ struct clk *ick;
+
+ /*** capture data ***/
+ /* file handle, if streaming is on */
+ struct file *streaming;
+};
+
+/* Per-file handle data. */
+struct omap24xxcam_fh {
+ spinlock_t vbq_lock; /* spinlock for the videobuf queue */
+ struct videobuf_queue vbq;
+ struct v4l2_pix_format pix; /* serialise pix by vbq->lock */
+ atomic_t field_count; /* field counter for videobuf_buffer */
+ /* accessing cam here doesn't need serialisation: it's constant */
+ struct omap24xxcam_device *cam;
+};
+
+/*
+ *
+ * Register I/O functions.
+ *
+ */
+
+static inline u32 omap24xxcam_reg_in(unsigned long base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline u32 omap24xxcam_reg_out(unsigned long base, u32 offset,
+ u32 val)
+{
+ writel(val, base + offset);
+ return val;
+}
+
+static inline u32 omap24xxcam_reg_merge(unsigned long base, u32 offset,
+ u32 val, u32 mask)
+{
+ u32 addr = base + offset;
+ u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+ writel(new_val, addr);
+ return new_val;
+}
+
+/*
+ *
+ * Function prototypes.
+ *
+ */
+
+/* dma prototypes */
+
+void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma);
+void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma);
+
+/* sgdma prototypes */
+
+void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma);
+int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
+ const struct scatterlist *sglist, int sglen,
+ int len, sgdma_callback_t callback, void *arg);
+void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma);
+void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
+ unsigned long base,
+ void (*reset_callback)(unsigned long data),
+ unsigned long reset_callback_data);
+void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma);
+
+#endif
diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c
index 210f1240b331..6ee9b69cc4a9 100644
--- a/drivers/media/video/ov511.c
+++ b/drivers/media/video/ov511.c
@@ -4011,8 +4011,7 @@ ov51x_v4l1_close(struct inode *inode, struct file *file)
/* Do not call this function directly! */
static int
-ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+ov51x_v4l1_ioctl_internal(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = file->private_data;
struct usb_ov511 *ov = video_get_drvdata(vdev);
@@ -4461,7 +4460,7 @@ ov51x_v4l1_ioctl(struct inode *inode, struct file *file,
if (mutex_lock_interruptible(&ov->lock))
return -EINTR;
- rc = video_usercopy(inode, file, cmd, arg, ov51x_v4l1_ioctl_internal);
+ rc = video_usercopy(file, cmd, arg, ov51x_v4l1_ioctl_internal);
mutex_unlock(&ov->lock);
return rc;
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
new file mode 100644
index 000000000000..54b736fcc07a
--- /dev/null
+++ b/drivers/media/video/ov772x.c
@@ -0,0 +1,1012 @@
+/*
+ * ov772x Camera Driver
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/soc_camera.h>
+#include <media/ov772x.h>
+
+/*
+ * register offset
+ */
+#define GAIN 0x00 /* AGC - Gain control gain setting */
+#define BLUE 0x01 /* AWB - Blue channel gain setting */
+#define RED 0x02 /* AWB - Red channel gain setting */
+#define GREEN 0x03 /* AWB - Green channel gain setting */
+#define COM1 0x04 /* Common control 1 */
+#define BAVG 0x05 /* U/B Average Level */
+#define GAVG 0x06 /* Y/Gb Average Level */
+#define RAVG 0x07 /* V/R Average Level */
+#define AECH 0x08 /* Exposure Value - AEC MSBs */
+#define COM2 0x09 /* Common control 2 */
+#define PID 0x0A /* Product ID Number MSB */
+#define VER 0x0B /* Product ID Number LSB */
+#define COM3 0x0C /* Common control 3 */
+#define COM4 0x0D /* Common control 4 */
+#define COM5 0x0E /* Common control 5 */
+#define COM6 0x0F /* Common control 6 */
+#define AEC 0x10 /* Exposure Value */
+#define CLKRC 0x11 /* Internal clock */
+#define COM7 0x12 /* Common control 7 */
+#define COM8 0x13 /* Common control 8 */
+#define COM9 0x14 /* Common control 9 */
+#define COM10 0x15 /* Common control 10 */
+#define REG16 0x16 /* Register 16 */
+#define HSTART 0x17 /* Horizontal sensor size */
+#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */
+#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */
+#define VSIZE 0x1A /* Vertical sensor size */
+#define PSHFT 0x1B /* Data format - pixel delay select */
+#define MIDH 0x1C /* Manufacturer ID byte - high */
+#define MIDL 0x1D /* Manufacturer ID byte - low */
+#define LAEC 0x1F /* Fine AEC value */
+#define COM11 0x20 /* Common control 11 */
+#define BDBASE 0x22 /* Banding filter Minimum AEC value */
+#define DBSTEP 0x23 /* Banding filter Maximum Setp */
+#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
+#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
+#define VPT 0x26 /* AGC/AEC Fast mode operating region */
+#define REG28 0x28 /* Register 28 */
+#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */
+#define EXHCH 0x2A /* Dummy pixel insert MSB */
+#define EXHCL 0x2B /* Dummy pixel insert LSB */
+#define VOUTSIZE 0x2C /* Vertical data output size MSBs */
+#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
+#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */
+#define YAVE 0x2F /* Y/G Channel Average value */
+#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */
+#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */
+#define HREF 0x32 /* Image start and size control */
+#define DM_LNL 0x33 /* Dummy line low 8 bits */
+#define DM_LNH 0x34 /* Dummy line high 8 bits */
+#define ADOFF_B 0x35 /* AD offset compensation value for B channel */
+#define ADOFF_R 0x36 /* AD offset compensation value for R channel */
+#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */
+#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */
+#define OFF_B 0x39 /* Analog process B channel offset value */
+#define OFF_R 0x3A /* Analog process R channel offset value */
+#define OFF_GB 0x3B /* Analog process Gb channel offset value */
+#define OFF_GR 0x3C /* Analog process Gr channel offset value */
+#define COM12 0x3D /* Common control 12 */
+#define COM13 0x3E /* Common control 13 */
+#define COM14 0x3F /* Common control 14 */
+#define COM15 0x40 /* Common control 15*/
+#define COM16 0x41 /* Common control 16 */
+#define TGT_B 0x42 /* BLC blue channel target value */
+#define TGT_R 0x43 /* BLC red channel target value */
+#define TGT_GB 0x44 /* BLC Gb channel target value */
+#define TGT_GR 0x45 /* BLC Gr channel target value */
+/* for ov7720 */
+#define LCC0 0x46 /* Lens correction control 0 */
+#define LCC1 0x47 /* Lens correction option 1 - X coordinate */
+#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */
+#define LCC3 0x49 /* Lens correction option 3 */
+#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */
+#define LCC5 0x4B /* Lens correction option 5 */
+#define LCC6 0x4C /* Lens correction option 6 */
+/* for ov7725 */
+#define LC_CTR 0x46 /* Lens correction control */
+#define LC_XC 0x47 /* X coordinate of lens correction center relative */
+#define LC_YC 0x48 /* Y coordinate of lens correction center relative */
+#define LC_COEF 0x49 /* Lens correction coefficient */
+#define LC_RADI 0x4A /* Lens correction radius */
+#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */
+#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */
+
+#define FIXGAIN 0x4D /* Analog fix gain amplifer */
+#define AREF0 0x4E /* Sensor reference control */
+#define AREF1 0x4F /* Sensor reference current control */
+#define AREF2 0x50 /* Analog reference control */
+#define AREF3 0x51 /* ADC reference control */
+#define AREF4 0x52 /* ADC reference control */
+#define AREF5 0x53 /* ADC reference control */
+#define AREF6 0x54 /* Analog reference control */
+#define AREF7 0x55 /* Analog reference control */
+#define UFIX 0x60 /* U channel fixed value output */
+#define VFIX 0x61 /* V channel fixed value output */
+#define AWBB_BLK 0x62 /* AWB option for advanced AWB */
+#define AWB_CTRL0 0x63 /* AWB control byte 0 */
+#define DSP_CTRL1 0x64 /* DSP control byte 1 */
+#define DSP_CTRL2 0x65 /* DSP control byte 2 */
+#define DSP_CTRL3 0x66 /* DSP control byte 3 */
+#define DSP_CTRL4 0x67 /* DSP control byte 4 */
+#define AWB_BIAS 0x68 /* AWB BLC level clip */
+#define AWB_CTRL1 0x69 /* AWB control 1 */
+#define AWB_CTRL2 0x6A /* AWB control 2 */
+#define AWB_CTRL3 0x6B /* AWB control 3 */
+#define AWB_CTRL4 0x6C /* AWB control 4 */
+#define AWB_CTRL5 0x6D /* AWB control 5 */
+#define AWB_CTRL6 0x6E /* AWB control 6 */
+#define AWB_CTRL7 0x6F /* AWB control 7 */
+#define AWB_CTRL8 0x70 /* AWB control 8 */
+#define AWB_CTRL9 0x71 /* AWB control 9 */
+#define AWB_CTRL10 0x72 /* AWB control 10 */
+#define AWB_CTRL11 0x73 /* AWB control 11 */
+#define AWB_CTRL12 0x74 /* AWB control 12 */
+#define AWB_CTRL13 0x75 /* AWB control 13 */
+#define AWB_CTRL14 0x76 /* AWB control 14 */
+#define AWB_CTRL15 0x77 /* AWB control 15 */
+#define AWB_CTRL16 0x78 /* AWB control 16 */
+#define AWB_CTRL17 0x79 /* AWB control 17 */
+#define AWB_CTRL18 0x7A /* AWB control 18 */
+#define AWB_CTRL19 0x7B /* AWB control 19 */
+#define AWB_CTRL20 0x7C /* AWB control 20 */
+#define AWB_CTRL21 0x7D /* AWB control 21 */
+#define GAM1 0x7E /* Gamma Curve 1st segment input end point */
+#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */
+#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */
+#define GAM4 0x81 /* Gamma Curve 4th segment input end point */
+#define GAM5 0x82 /* Gamma Curve 5th segment input end point */
+#define GAM6 0x83 /* Gamma Curve 6th segment input end point */
+#define GAM7 0x84 /* Gamma Curve 7th segment input end point */
+#define GAM8 0x85 /* Gamma Curve 8th segment input end point */
+#define GAM9 0x86 /* Gamma Curve 9th segment input end point */
+#define GAM10 0x87 /* Gamma Curve 10th segment input end point */
+#define GAM11 0x88 /* Gamma Curve 11th segment input end point */
+#define GAM12 0x89 /* Gamma Curve 12th segment input end point */
+#define GAM13 0x8A /* Gamma Curve 13th segment input end point */
+#define GAM14 0x8B /* Gamma Curve 14th segment input end point */
+#define GAM15 0x8C /* Gamma Curve 15th segment input end point */
+#define SLOP 0x8D /* Gamma curve highest segment slope */
+#define DNSTH 0x8E /* De-noise threshold */
+#define EDGE0 0x8F /* Edge enhancement control 0 */
+#define EDGE1 0x90 /* Edge enhancement control 1 */
+#define DNSOFF 0x91 /* Auto De-noise threshold control */
+#define EDGE2 0x92 /* Edge enhancement strength low point control */
+#define EDGE3 0x93 /* Edge enhancement strength high point control */
+#define MTX1 0x94 /* Matrix coefficient 1 */
+#define MTX2 0x95 /* Matrix coefficient 2 */
+#define MTX3 0x96 /* Matrix coefficient 3 */
+#define MTX4 0x97 /* Matrix coefficient 4 */
+#define MTX5 0x98 /* Matrix coefficient 5 */
+#define MTX6 0x99 /* Matrix coefficient 6 */
+#define MTX_CTRL 0x9A /* Matrix control */
+#define BRIGHT 0x9B /* Brightness control */
+#define CNTRST 0x9C /* Contrast contrast */
+#define CNTRST_CTRL 0x9D /* Contrast contrast center */
+#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */
+#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */
+#define SCAL0 0xA0 /* Scaling control 0 */
+#define SCAL1 0xA1 /* Scaling control 1 */
+#define SCAL2 0xA2 /* Scaling control 2 */
+#define FIFODLYM 0xA3 /* FIFO manual mode delay control */
+#define FIFODLYA 0xA4 /* FIFO auto mode delay control */
+#define SDE 0xA6 /* Special digital effect control */
+#define USAT 0xA7 /* U component saturation control */
+#define VSAT 0xA8 /* V component saturation control */
+/* for ov7720 */
+#define HUE0 0xA9 /* Hue control 0 */
+#define HUE1 0xAA /* Hue control 1 */
+/* for ov7725 */
+#define HUECOS 0xA9 /* Cosine value */
+#define HUESIN 0xAA /* Sine value */
+
+#define SIGN 0xAB /* Sign bit for Hue and contrast */
+#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */
+
+/*
+ * register detail
+ */
+
+/* COM2 */
+#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
+ /* Output drive capability */
+#define OCAP_1x 0x00 /* 1x */
+#define OCAP_2x 0x01 /* 2x */
+#define OCAP_3x 0x02 /* 3x */
+#define OCAP_4x 0x03 /* 4x */
+
+/* COM3 */
+#define SWAP_MASK 0x38
+
+#define VFIMG_ON_OFF 0x80 /* Vertical flip image ON/OFF selection */
+#define HMIMG_ON_OFF 0x40 /* Horizontal mirror image ON/OFF selection */
+#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */
+#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */
+#define SWAP_ML 0x08 /* Swap output MSB/LSB */
+ /* Tri-state option for output clock */
+#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */
+ /* 1: No tri-state at this period */
+ /* Tri-state option for output data */
+#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */
+ /* 1: No tri-state at this period */
+#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */
+
+/* COM4 */
+ /* PLL frequency control */
+#define PLL_BYPASS 0x00 /* 00: Bypass PLL */
+#define PLL_4x 0x40 /* 01: PLL 4x */
+#define PLL_6x 0x80 /* 10: PLL 6x */
+#define PLL_8x 0xc0 /* 11: PLL 8x */
+ /* AEC evaluate window */
+#define AEC_FULL 0x00 /* 00: Full window */
+#define AEC_1p2 0x10 /* 01: 1/2 window */
+#define AEC_1p4 0x20 /* 10: 1/4 window */
+#define AEC_2p3 0x30 /* 11: Low 2/3 window */
+
+/* COM5 */
+#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */
+#define AFR_SPPED 0x40 /* Auto frame rate control speed slection */
+ /* Auto frame rate max rate control */
+#define AFR_NO_RATE 0x00 /* No reduction of frame rate */
+#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */
+#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */
+#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */
+ /* Auto frame rate active point control */
+#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */
+#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */
+#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */
+#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */
+ /* AEC max step control */
+#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */
+ /* 1 : No limit to AEC increase step */
+
+/* COM7 */
+ /* SCCB Register Reset */
+#define SCCB_RESET 0x80 /* 0 : No change */
+ /* 1 : Resets all registers to default */
+ /* Resolution selection */
+#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */
+#define SLCT_VGA 0x00 /* 0 : VGA */
+#define SLCT_QVGA 0x40 /* 1 : QVGA */
+#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */
+ /* RGB output format control */
+#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */
+#define FMT_RGB565 0x04 /* 01 : RGB 565 */
+#define FMT_RGB555 0x08 /* 10 : RGB 555 */
+#define FMT_RGB444 0x0c /* 11 : RGB 444 */
+ /* Output format control */
+#define OFMT_YUV 0x00 /* 00 : YUV */
+#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */
+#define OFMT_RGB 0x02 /* 10 : RGB */
+#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */
+
+/* COM8 */
+#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */
+ /* AEC Setp size limit */
+#define UNLMT_STEP 0x40 /* 0 : Step size is limited */
+ /* 1 : Unlimited step size */
+#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */
+#define AEC_BND 0x10 /* Enable AEC below banding value */
+#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */
+#define AGC_ON 0x04 /* AGC Enable */
+#define AWB_ON 0x02 /* AWB Enable */
+#define AEC_ON 0x01 /* AEC Enable */
+
+/* COM9 */
+#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */
+ /* Automatic gain ceiling - maximum AGC value */
+#define GAIN_2x 0x00 /* 000 : 2x */
+#define GAIN_4x 0x10 /* 001 : 4x */
+#define GAIN_8x 0x20 /* 010 : 8x */
+#define GAIN_16x 0x30 /* 011 : 16x */
+#define GAIN_32x 0x40 /* 100 : 32x */
+#define GAIN_64x 0x50 /* 101 : 64x */
+#define GAIN_128x 0x60 /* 110 : 128x */
+#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
+#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
+
+/* COM11 */
+#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */
+#define SGLF_TRIG 0x01 /* Single frame transfer trigger */
+
+/* EXHCH */
+#define VSIZE_LSB 0x04 /* Vertical data output size LSB */
+
+/* DSP_CTRL1 */
+#define FIFO_ON 0x80 /* FIFO enable/disable selection */
+#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */
+#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */
+#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */
+#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */
+#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */
+#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */
+#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */
+
+/* DSP_CTRL3 */
+#define UV_MASK 0x80 /* UV output sequence option */
+#define UV_ON 0x80 /* ON */
+#define UV_OFF 0x00 /* OFF */
+#define CBAR_MASK 0x20 /* DSP Color bar mask */
+#define CBAR_ON 0x20 /* ON */
+#define CBAR_OFF 0x00 /* OFF */
+
+/* HSTART */
+#define HST_VGA 0x23
+#define HST_QVGA 0x3F
+
+/* HSIZE */
+#define HSZ_VGA 0xA0
+#define HSZ_QVGA 0x50
+
+/* VSTART */
+#define VST_VGA 0x07
+#define VST_QVGA 0x03
+
+/* VSIZE */
+#define VSZ_VGA 0xF0
+#define VSZ_QVGA 0x78
+
+/* HOUTSIZE */
+#define HOSZ_VGA 0xA0
+#define HOSZ_QVGA 0x50
+
+/* VOUTSIZE */
+#define VOSZ_VGA 0xF0
+#define VOSZ_QVGA 0x78
+
+/*
+ * bit configure (32 bit)
+ * this is used in struct ov772x_color_format :: option
+ */
+#define OP_UV 0x00000001
+#define OP_SWAP_RGB 0x00000002
+
+/*
+ * ID
+ */
+#define OV7720 0x7720
+#define OV7725 0x7721
+#define VERSION(pid, ver) ((pid<<8)|(ver&0xFF))
+
+/*
+ * struct
+ */
+struct regval_list {
+ unsigned char reg_num;
+ unsigned char value;
+};
+
+struct ov772x_color_format {
+ char *name;
+ __u32 fourcc;
+ const struct regval_list *regs;
+ unsigned int option;
+};
+
+struct ov772x_win_size {
+ char *name;
+ __u32 width;
+ __u32 height;
+ unsigned char com7_bit;
+ const struct regval_list *regs;
+};
+
+struct ov772x_priv {
+ struct ov772x_camera_info *info;
+ struct i2c_client *client;
+ struct soc_camera_device icd;
+ const struct ov772x_color_format *fmt;
+ const struct ov772x_win_size *win;
+ int model;
+};
+
+#define ENDMARKER { 0xff, 0xff }
+
+/*
+ * register setting for color format
+ */
+static const struct regval_list ov772x_RGB555_regs[] = {
+ { COM3, 0x00 },
+ { COM7, FMT_RGB555 | OFMT_RGB },
+ ENDMARKER,
+};
+
+static const struct regval_list ov772x_RGB565_regs[] = {
+ { COM3, 0x00 },
+ { COM7, FMT_RGB565 | OFMT_RGB },
+ ENDMARKER,
+};
+
+static const struct regval_list ov772x_YYUV_regs[] = {
+ { COM3, SWAP_YUV },
+ { COM7, OFMT_YUV },
+ ENDMARKER,
+};
+
+static const struct regval_list ov772x_UVYY_regs[] = {
+ { COM3, 0x00 },
+ { COM7, OFMT_YUV },
+ ENDMARKER,
+};
+
+
+/*
+ * register setting for window size
+ */
+static const struct regval_list ov772x_qvga_regs[] = {
+ { HSTART, HST_QVGA },
+ { HSIZE, HSZ_QVGA },
+ { VSTART, VST_QVGA },
+ { VSIZE, VSZ_QVGA },
+ { HOUTSIZE, HOSZ_QVGA },
+ { VOUTSIZE, VOSZ_QVGA },
+ ENDMARKER,
+};
+
+static const struct regval_list ov772x_vga_regs[] = {
+ { HSTART, HST_VGA },
+ { HSIZE, HSZ_VGA },
+ { VSTART, VST_VGA },
+ { VSIZE, VSZ_VGA },
+ { HOUTSIZE, HOSZ_VGA },
+ { VOUTSIZE, VOSZ_VGA },
+ ENDMARKER,
+};
+
+/*
+ * supported format list
+ */
+
+#define SETFOURCC(type) .name = (#type), .fourcc = (V4L2_PIX_FMT_ ## type)
+static const struct soc_camera_data_format ov772x_fmt_lists[] = {
+ {
+ SETFOURCC(YUYV),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ SETFOURCC(YVYU),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ SETFOURCC(UYVY),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ SETFOURCC(RGB555),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+ {
+ SETFOURCC(RGB555X),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+ {
+ SETFOURCC(RGB565),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+ {
+ SETFOURCC(RGB565X),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+};
+
+/*
+ * color format list
+ */
+#define T_YUYV 0
+static const struct ov772x_color_format ov772x_cfmts[] = {
+ [T_YUYV] = {
+ SETFOURCC(YUYV),
+ .regs = ov772x_YYUV_regs,
+ },
+ {
+ SETFOURCC(YVYU),
+ .regs = ov772x_YYUV_regs,
+ .option = OP_UV,
+ },
+ {
+ SETFOURCC(UYVY),
+ .regs = ov772x_UVYY_regs,
+ },
+ {
+ SETFOURCC(RGB555),
+ .regs = ov772x_RGB555_regs,
+ .option = OP_SWAP_RGB,
+ },
+ {
+ SETFOURCC(RGB555X),
+ .regs = ov772x_RGB555_regs,
+ },
+ {
+ SETFOURCC(RGB565),
+ .regs = ov772x_RGB565_regs,
+ .option = OP_SWAP_RGB,
+ },
+ {
+ SETFOURCC(RGB565X),
+ .regs = ov772x_RGB565_regs,
+ },
+};
+
+
+/*
+ * window size list
+ */
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+#define QVGA_WIDTH 320
+#define QVGA_HEIGHT 240
+#define MAX_WIDTH VGA_WIDTH
+#define MAX_HEIGHT VGA_HEIGHT
+
+static const struct ov772x_win_size ov772x_win_vga = {
+ .name = "VGA",
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .com7_bit = SLCT_VGA,
+ .regs = ov772x_vga_regs,
+};
+
+static const struct ov772x_win_size ov772x_win_qvga = {
+ .name = "QVGA",
+ .width = QVGA_WIDTH,
+ .height = QVGA_HEIGHT,
+ .com7_bit = SLCT_QVGA,
+ .regs = ov772x_qvga_regs,
+};
+
+
+/*
+ * general function
+ */
+
+static int ov772x_write_array(struct i2c_client *client,
+ const struct regval_list *vals)
+{
+ while (vals->reg_num != 0xff) {
+ int ret = i2c_smbus_write_byte_data(client,
+ vals->reg_num,
+ vals->value);
+ if (ret < 0)
+ return ret;
+ vals++;
+ }
+ return 0;
+}
+
+static int ov772x_mask_set(struct i2c_client *client,
+ u8 command,
+ u8 mask,
+ u8 set)
+{
+ s32 val = i2c_smbus_read_byte_data(client, command);
+ val &= ~mask;
+ val |= set;
+
+ return i2c_smbus_write_byte_data(client, command, val);
+}
+
+static int ov772x_reset(struct i2c_client *client)
+{
+ int ret = i2c_smbus_write_byte_data(client, COM7, SCCB_RESET);
+ msleep(1);
+ return ret;
+}
+
+/*
+ * soc_camera_ops function
+ */
+
+static int ov772x_init(struct soc_camera_device *icd)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ int ret = 0;
+
+ if (priv->info->link.power) {
+ ret = priv->info->link.power(&priv->client->dev, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (priv->info->link.reset)
+ ret = priv->info->link.reset(&priv->client->dev);
+
+ return ret;
+}
+
+static int ov772x_release(struct soc_camera_device *icd)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ int ret = 0;
+
+ if (priv->info->link.power)
+ ret = priv->info->link.power(&priv->client->dev, 0);
+
+ return ret;
+}
+
+static int ov772x_start_capture(struct soc_camera_device *icd)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ int ret;
+
+ if (!priv->win)
+ priv->win = &ov772x_win_vga;
+ if (!priv->fmt)
+ priv->fmt = &ov772x_cfmts[T_YUYV];
+
+ /*
+ * reset hardware
+ */
+ ov772x_reset(priv->client);
+
+ /*
+ * set color format
+ */
+ ret = ov772x_write_array(priv->client, priv->fmt->regs);
+ if (ret < 0)
+ goto start_end;
+
+ /*
+ * set size format
+ */
+ ret = ov772x_write_array(priv->client, priv->win->regs);
+ if (ret < 0)
+ goto start_end;
+
+ /*
+ * set COM7 bit ( QVGA or VGA )
+ */
+ ret = ov772x_mask_set(priv->client,
+ COM7, SLCT_MASK, priv->win->com7_bit);
+ if (ret < 0)
+ goto start_end;
+
+ /*
+ * set UV setting
+ */
+ if (priv->fmt->option & OP_UV) {
+ ret = ov772x_mask_set(priv->client,
+ DSP_CTRL3, UV_MASK, UV_ON);
+ if (ret < 0)
+ goto start_end;
+ }
+
+ /*
+ * set SWAP setting
+ */
+ if (priv->fmt->option & OP_SWAP_RGB) {
+ ret = ov772x_mask_set(priv->client,
+ COM3, SWAP_MASK, SWAP_RGB);
+ if (ret < 0)
+ goto start_end;
+ }
+
+ dev_dbg(&icd->dev,
+ "format %s, win %s\n", priv->fmt->name, priv->win->name);
+
+start_end:
+ priv->fmt = NULL;
+ priv->win = NULL;
+
+ return ret;
+}
+
+static int ov772x_stop_capture(struct soc_camera_device *icd)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ ov772x_reset(priv->client);
+ return 0;
+}
+
+static int ov772x_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+{
+ return 0;
+}
+
+static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ struct soc_camera_link *icl = priv->client->dev.platform_data;
+ unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
+ priv->info->buswidth;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+static int ov772x_get_chip_id(struct soc_camera_device *icd,
+ struct v4l2_chip_ident *id)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+
+ id->ident = priv->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov772x_get_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ int ret;
+
+ if (reg->reg > 0xff)
+ return -EINVAL;
+
+ ret = i2c_smbus_read_byte_data(priv->client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int ov772x_set_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+
+ if (reg->reg > 0xff ||
+ reg->val > 0xff)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val);
+}
+#endif
+
+static const struct ov772x_win_size*
+ov772x_select_win(u32 width, u32 height)
+{
+ __u32 diff;
+ const struct ov772x_win_size *win;
+
+ /* default is QVGA */
+ diff = abs(width - ov772x_win_qvga.width) +
+ abs(height - ov772x_win_qvga.height);
+ win = &ov772x_win_qvga;
+
+ /* VGA */
+ if (diff >
+ abs(width - ov772x_win_vga.width) +
+ abs(height - ov772x_win_vga.height))
+ win = &ov772x_win_vga;
+
+ return win;
+}
+
+
+static int ov772x_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt,
+ struct v4l2_rect *rect)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ int ret = -EINVAL;
+ int i;
+
+ /*
+ * select format
+ */
+ priv->fmt = NULL;
+ for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
+ if (pixfmt == ov772x_cfmts[i].fourcc) {
+ priv->fmt = ov772x_cfmts + i;
+ ret = 0;
+ break;
+ }
+ }
+
+ /*
+ * select win
+ */
+ priv->win = ov772x_select_win(rect->width, rect->height);
+
+ return ret;
+}
+
+static int ov772x_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ const struct ov772x_win_size *win;
+
+ /*
+ * select suitable win
+ */
+ win = ov772x_select_win(pix->width, pix->height);
+
+ pix->width = win->width;
+ pix->height = win->height;
+ pix->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov772x_video_probe(struct soc_camera_device *icd)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ u8 pid, ver;
+ const char *devname;
+
+ /*
+ * We must have a parent by now. And it cannot be a wrong one.
+ * So this entire test is completely redundant.
+ */
+ if (!icd->dev.parent ||
+ to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
+ return -ENODEV;
+
+ /*
+ * ov772x only use 8 or 10 bit bus width
+ */
+ if (SOCAM_DATAWIDTH_10 != priv->info->buswidth &&
+ SOCAM_DATAWIDTH_8 != priv->info->buswidth) {
+ dev_err(&icd->dev, "bus width error\n");
+ return -ENODEV;
+ }
+
+ icd->formats = ov772x_fmt_lists;
+ icd->num_formats = ARRAY_SIZE(ov772x_fmt_lists);
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+ pid = i2c_smbus_read_byte_data(priv->client, PID);
+ ver = i2c_smbus_read_byte_data(priv->client, VER);
+
+ switch (VERSION(pid, ver)) {
+ case OV7720:
+ devname = "ov7720";
+ priv->model = V4L2_IDENT_OV7720;
+ break;
+ case OV7725:
+ devname = "ov7725";
+ priv->model = V4L2_IDENT_OV7725;
+ break;
+ default:
+ dev_err(&icd->dev,
+ "Product ID error %x:%x\n", pid, ver);
+ return -ENODEV;
+ }
+
+ dev_info(&icd->dev,
+ "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
+ devname,
+ pid,
+ ver,
+ i2c_smbus_read_byte_data(priv->client, MIDH),
+ i2c_smbus_read_byte_data(priv->client, MIDL));
+
+
+ return soc_camera_video_start(icd);
+}
+
+static void ov772x_video_remove(struct soc_camera_device *icd)
+{
+ soc_camera_video_stop(icd);
+}
+
+static struct soc_camera_ops ov772x_ops = {
+ .owner = THIS_MODULE,
+ .probe = ov772x_video_probe,
+ .remove = ov772x_video_remove,
+ .init = ov772x_init,
+ .release = ov772x_release,
+ .start_capture = ov772x_start_capture,
+ .stop_capture = ov772x_stop_capture,
+ .set_fmt = ov772x_set_fmt,
+ .try_fmt = ov772x_try_fmt,
+ .set_bus_param = ov772x_set_bus_param,
+ .query_bus_param = ov772x_query_bus_param,
+ .get_chip_id = ov772x_get_chip_id,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .get_register = ov772x_get_register,
+ .set_register = ov772x_set_register,
+#endif
+};
+
+/*
+ * i2c_driver function
+ */
+
+static int ov772x_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov772x_priv *priv;
+ struct ov772x_camera_info *info;
+ struct soc_camera_device *icd;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
+
+ info = client->dev.platform_data;
+ if (!info)
+ return -EINVAL;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&adapter->dev,
+ "I2C-Adapter doesn't support "
+ "I2C_FUNC_SMBUS_BYTE_DATA\n");
+ return -EIO;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = info;
+ priv->client = client;
+ i2c_set_clientdata(client, priv);
+
+ icd = &priv->icd;
+ icd->ops = &ov772x_ops;
+ icd->control = &client->dev;
+ icd->width_max = MAX_WIDTH;
+ icd->height_max = MAX_HEIGHT;
+ icd->iface = priv->info->link.bus_id;
+
+ ret = soc_camera_device_register(icd);
+
+ if (ret) {
+ i2c_set_clientdata(client, NULL);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int ov772x_remove(struct i2c_client *client)
+{
+ struct ov772x_priv *priv = i2c_get_clientdata(client);
+
+ soc_camera_device_unregister(&priv->icd);
+ i2c_set_clientdata(client, NULL);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id ov772x_id[] = {
+ { "ov772x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov772x_id);
+
+static struct i2c_driver ov772x_i2c_driver = {
+ .driver = {
+ .name = "ov772x",
+ },
+ .probe = ov772x_probe,
+ .remove = ov772x_remove,
+ .id_table = ov772x_id,
+};
+
+/*
+ * module function
+ */
+
+static int __init ov772x_module_init(void)
+{
+ return i2c_add_driver(&ov772x_i2c_driver);
+}
+
+static void __exit ov772x_module_exit(void)
+{
+ i2c_del_driver(&ov772x_i2c_driver);
+}
+
+module_init(ov772x_module_init);
+module_exit(ov772x_module_exit);
+
+MODULE_DESCRIPTION("SoC Camera driver for ov772x");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c
index 994807818aa2..45730fac1570 100644
--- a/drivers/media/video/pms.c
+++ b/drivers/media/video/pms.c
@@ -10,8 +10,8 @@
* 14478 Potsdam, Germany
*
* Most of this code is directly derived from his userspace driver.
- * His driver works so send any reports to alan@redhat.com unless the
- * userspace driver also doesn't work for you...
+ * His driver works so send any reports to alan@lxorguk.ukuu.org.uk
+ * unless the userspace driver also doesn't work for you...
*
* Changes:
* 08/07/2003 Daniele Bellucci <bellucda@tiscali.it>
@@ -680,8 +680,7 @@ static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int
* Video4linux interfacing
*/
-static int pms_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int pms_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *dev = video_devdata(file);
struct pms_device *pd=(struct pms_device *)dev;
@@ -866,7 +865,7 @@ static int pms_do_ioctl(struct inode *inode, struct file *file,
static int pms_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, pms_do_ioctl);
+ return video_usercopy(file, cmd, arg, pms_do_ioctl);
}
static ssize_t pms_read(struct file *file, char __user *buf,
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 5b81ba469641..4358079f1966 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -2395,7 +2395,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
scnprintf(hdw->bus_info,sizeof(hdw->bus_info),
"usb %s address %d",
- hdw->usb_dev->dev.bus_id,
+ dev_name(&hdw->usb_dev->dev),
hdw->usb_dev->devnum);
ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index 733680f21317..e641cd971453 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -628,10 +628,10 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
class_dev->class = &class_ptr->class;
if (pvr2_hdw_get_sn(sfp->channel.hdw)) {
- snprintf(class_dev->bus_id, BUS_ID_SIZE, "sn-%lu",
+ dev_set_name(class_dev, "sn-%lu",
pvr2_hdw_get_sn(sfp->channel.hdw));
} else if (pvr2_hdw_get_unit_number(sfp->channel.hdw) >= 0) {
- snprintf(class_dev->bus_id, BUS_ID_SIZE, "unit-%c",
+ dev_set_name(class_dev, "unit-%c",
pvr2_hdw_get_unit_number(sfp->channel.hdw) + 'a');
} else {
kfree(class_dev);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 97ed95957992..52af1c435965 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -168,8 +168,7 @@ static const char *get_v4l_name(int v4l_type)
* This is part of Video 4 Linux API. The procedure handles ioctl() calls.
*
*/
-static int __pvr2_v4l2_do_ioctl(struct file *file,
- unsigned int cmd, void *arg)
+static int pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_v4l2 *vp = fh->vhead;
@@ -864,7 +863,7 @@ static int __pvr2_v4l2_do_ioctl(struct file *file,
default :
ret = v4l_compat_translate_ioctl(file, cmd,
- arg, __pvr2_v4l2_do_ioctl);
+ arg, pvr2_v4l2_do_ioctl);
}
pvr2_hdw_commit_ctl(hdw);
@@ -890,12 +889,6 @@ static int __pvr2_v4l2_do_ioctl(struct file *file,
return ret;
}
-static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
-{
- return __pvr2_v4l2_do_ioctl(file, cmd, arg);
-}
-
static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
{
int num = dip->devbase.num;
@@ -963,7 +956,7 @@ static int pvr2_v4l2_ioctl(struct inode *inode, struct file *file,
#define IVTV_IOC_G_CODEC 0xFFEE7703
#define IVTV_IOC_S_CODEC 0xFFEE7704
if (cmd == IVTV_IOC_G_CODEC || cmd == IVTV_IOC_S_CODEC) return 0;
- return video_usercopy(inode, file, cmd, arg, pvr2_v4l2_do_ioctl);
+ return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl);
}
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index f3897a3fdb75..1ce9da167b7e 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -1412,7 +1412,7 @@ static int pwc_video_ioctl(struct inode *inode, struct file *file,
mutex_lock(&pdev->modlock);
if (!pdev->unplugged)
- r = video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl);
+ r = video_usercopy(file, cmd, arg, pwc_video_do_ioctl);
mutex_unlock(&pdev->modlock);
out:
return r;
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index 76a1376c9751..d7c147328e35 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -337,8 +337,7 @@ static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f)
}
-int pwc_video_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+int pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = video_devdata(file);
struct pwc_device *pdev;
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
index 74178754b39b..c046a2535668 100644
--- a/drivers/media/video/pwc/pwc.h
+++ b/drivers/media/video/pwc/pwc.h
@@ -340,8 +340,7 @@ extern int pwc_camera_power(struct pwc_device *pdev, int power);
extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg);
/** Functions in pwc-v4l.c */
-extern int pwc_video_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg);
+extern int pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg);
/** pwc-uncompress.c */
/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index eb6be5802928..9d33de22cc48 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -25,7 +25,6 @@
#include <linux/version.h>
#include <linux/device.h>
#include <linux/platform_device.h>
-#include <linux/mutex.h>
#include <linux/clk.h>
#include <media/v4l2-common.h>
@@ -39,9 +38,106 @@
#include <mach/pxa-regs.h>
#include <mach/camera.h>
+#include "pxa_camera.h"
+
#define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5)
#define PXA_CAM_DRV_NAME "pxa27x-camera"
+/* Camera Interface */
+#define CICR0 0x0000
+#define CICR1 0x0004
+#define CICR2 0x0008
+#define CICR3 0x000C
+#define CICR4 0x0010
+#define CISR 0x0014
+#define CIFR 0x0018
+#define CITOR 0x001C
+#define CIBR0 0x0028
+#define CIBR1 0x0030
+#define CIBR2 0x0038
+
+#define CICR0_DMAEN (1 << 31) /* DMA request enable */
+#define CICR0_PAR_EN (1 << 30) /* Parity enable */
+#define CICR0_SL_CAP_EN (1 << 29) /* Capture enable for slave mode */
+#define CICR0_ENB (1 << 28) /* Camera interface enable */
+#define CICR0_DIS (1 << 27) /* Camera interface disable */
+#define CICR0_SIM (0x7 << 24) /* Sensor interface mode mask */
+#define CICR0_TOM (1 << 9) /* Time-out mask */
+#define CICR0_RDAVM (1 << 8) /* Receive-data-available mask */
+#define CICR0_FEM (1 << 7) /* FIFO-empty mask */
+#define CICR0_EOLM (1 << 6) /* End-of-line mask */
+#define CICR0_PERRM (1 << 5) /* Parity-error mask */
+#define CICR0_QDM (1 << 4) /* Quick-disable mask */
+#define CICR0_CDM (1 << 3) /* Disable-done mask */
+#define CICR0_SOFM (1 << 2) /* Start-of-frame mask */
+#define CICR0_EOFM (1 << 1) /* End-of-frame mask */
+#define CICR0_FOM (1 << 0) /* FIFO-overrun mask */
+
+#define CICR1_TBIT (1 << 31) /* Transparency bit */
+#define CICR1_RGBT_CONV (0x3 << 29) /* RGBT conversion mask */
+#define CICR1_PPL (0x7ff << 15) /* Pixels per line mask */
+#define CICR1_RGB_CONV (0x7 << 12) /* RGB conversion mask */
+#define CICR1_RGB_F (1 << 11) /* RGB format */
+#define CICR1_YCBCR_F (1 << 10) /* YCbCr format */
+#define CICR1_RGB_BPP (0x7 << 7) /* RGB bis per pixel mask */
+#define CICR1_RAW_BPP (0x3 << 5) /* Raw bis per pixel mask */
+#define CICR1_COLOR_SP (0x3 << 3) /* Color space mask */
+#define CICR1_DW (0x7 << 0) /* Data width mask */
+
+#define CICR2_BLW (0xff << 24) /* Beginning-of-line pixel clock
+ wait count mask */
+#define CICR2_ELW (0xff << 16) /* End-of-line pixel clock
+ wait count mask */
+#define CICR2_HSW (0x3f << 10) /* Horizontal sync pulse width mask */
+#define CICR2_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock
+ wait count mask */
+#define CICR2_FSW (0x7 << 0) /* Frame stabilization
+ wait count mask */
+
+#define CICR3_BFW (0xff << 24) /* Beginning-of-frame line clock
+ wait count mask */
+#define CICR3_EFW (0xff << 16) /* End-of-frame line clock
+ wait count mask */
+#define CICR3_VSW (0x3f << 10) /* Vertical sync pulse width mask */
+#define CICR3_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock
+ wait count mask */
+#define CICR3_LPF (0x7ff << 0) /* Lines per frame mask */
+
+#define CICR4_MCLK_DLY (0x3 << 24) /* MCLK Data Capture Delay mask */
+#define CICR4_PCLK_EN (1 << 23) /* Pixel clock enable */
+#define CICR4_PCP (1 << 22) /* Pixel clock polarity */
+#define CICR4_HSP (1 << 21) /* Horizontal sync polarity */
+#define CICR4_VSP (1 << 20) /* Vertical sync polarity */
+#define CICR4_MCLK_EN (1 << 19) /* MCLK enable */
+#define CICR4_FR_RATE (0x7 << 8) /* Frame rate mask */
+#define CICR4_DIV (0xff << 0) /* Clock divisor mask */
+
+#define CISR_FTO (1 << 15) /* FIFO time-out */
+#define CISR_RDAV_2 (1 << 14) /* Channel 2 receive data available */
+#define CISR_RDAV_1 (1 << 13) /* Channel 1 receive data available */
+#define CISR_RDAV_0 (1 << 12) /* Channel 0 receive data available */
+#define CISR_FEMPTY_2 (1 << 11) /* Channel 2 FIFO empty */
+#define CISR_FEMPTY_1 (1 << 10) /* Channel 1 FIFO empty */
+#define CISR_FEMPTY_0 (1 << 9) /* Channel 0 FIFO empty */
+#define CISR_EOL (1 << 8) /* End of line */
+#define CISR_PAR_ERR (1 << 7) /* Parity error */
+#define CISR_CQD (1 << 6) /* Camera interface quick disable */
+#define CISR_CDD (1 << 5) /* Camera interface disable done */
+#define CISR_SOF (1 << 4) /* Start of frame */
+#define CISR_EOF (1 << 3) /* End of frame */
+#define CISR_IFO_2 (1 << 2) /* FIFO overrun for Channel 2 */
+#define CISR_IFO_1 (1 << 1) /* FIFO overrun for Channel 1 */
+#define CISR_IFO_0 (1 << 0) /* FIFO overrun for Channel 0 */
+
+#define CIFR_FLVL2 (0x7f << 23) /* FIFO 2 level mask */
+#define CIFR_FLVL1 (0x7f << 16) /* FIFO 1 level mask */
+#define CIFR_FLVL0 (0xff << 8) /* FIFO 0 level mask */
+#define CIFR_THL_0 (0x3 << 4) /* Threshold Level for Channel 0 FIFO */
+#define CIFR_RESET_F (1 << 3) /* Reset input FIFOs */
+#define CIFR_FEN2 (1 << 2) /* FIFO enable for channel 2 */
+#define CIFR_FEN1 (1 << 1) /* FIFO enable for channel 1 */
+#define CIFR_FEN0 (1 << 0) /* FIFO enable for channel 0 */
+
#define CICR0_SIM_MP (0 << 24)
#define CICR0_SIM_SP (1 << 24)
#define CICR0_SIM_MS (2 << 24)
@@ -69,8 +165,6 @@
CICR0_PERRM | CICR0_QDM | CICR0_CDM | CICR0_SOFM | \
CICR0_EOFM | CICR0_FOM)
-static DEFINE_MUTEX(camera_lock);
-
/*
* Structures
*/
@@ -120,7 +214,9 @@ struct pxa_camera_dev {
struct pxacamera_platform_data *pdata;
struct resource *res;
unsigned long platform_flags;
- unsigned long platform_mclk_10khz;
+ unsigned long ciclk;
+ unsigned long mclk;
+ u32 mclk_divisor;
struct list_head capture;
@@ -143,8 +239,7 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size);
@@ -170,8 +265,7 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
int i;
@@ -247,8 +341,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
struct videobuf_buffer *vb, enum v4l2_field field)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
int ret;
@@ -367,8 +460,7 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
struct pxa_buffer *active;
@@ -385,7 +477,10 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
active = pcdev->active;
if (!active) {
- CIFR |= CIFR_RESET_F;
+ unsigned long cifr, cicr0;
+
+ cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
+ __raw_writel(cifr, pcdev->base + CIFR);
for (i = 0; i < pcdev->channels; i++) {
DDADR(pcdev->dma_chans[i]) = buf->dmas[i].sg_dma;
@@ -394,7 +489,9 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
}
pcdev->active = buf;
- CICR0 |= CICR0_ENB;
+
+ cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB;
+ __raw_writel(cicr0, pcdev->base + CICR0);
} else {
struct pxa_cam_dma *buf_dma;
struct pxa_cam_dma *act_dma;
@@ -478,6 +575,8 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
struct videobuf_buffer *vb,
struct pxa_buffer *buf)
{
+ unsigned long cicr0;
+
/* _init is used to debug races, see comment in pxa_camera_reqbufs() */
list_del_init(&vb->queue);
vb->state = VIDEOBUF_DONE;
@@ -490,7 +589,9 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
DCSR(pcdev->dma_chans[0]) = 0;
DCSR(pcdev->dma_chans[1]) = 0;
DCSR(pcdev->dma_chans[2]) = 0;
- CICR0 &= ~CICR0_ENB;
+
+ cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB;
+ __raw_writel(cicr0, pcdev->base + CICR0);
return;
}
@@ -505,6 +606,7 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
unsigned long flags;
u32 status, camera_status, overrun;
struct videobuf_buffer *vb;
+ unsigned long cifr, cicr0;
spin_lock_irqsave(&pcdev->lock, flags);
@@ -527,22 +629,26 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
goto out;
}
- camera_status = CISR;
+ camera_status = __raw_readl(pcdev->base + CISR);
overrun = CISR_IFO_0;
if (pcdev->channels == 3)
overrun |= CISR_IFO_1 | CISR_IFO_2;
if (camera_status & overrun) {
dev_dbg(pcdev->dev, "FIFO overrun! CISR: %x\n", camera_status);
/* Stop the Capture Interface */
- CICR0 &= ~CICR0_ENB;
+ cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB;
+ __raw_writel(cicr0, pcdev->base + CICR0);
+
/* Stop DMA */
DCSR(channel) = 0;
/* Reset the FIFOs */
- CIFR |= CIFR_RESET_F;
+ cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
+ __raw_writel(cifr, pcdev->base + CIFR);
/* Enable End-Of-Frame Interrupt */
- CICR0 &= ~CICR0_EOFM;
+ cicr0 &= ~CICR0_EOFM;
+ __raw_writel(cicr0, pcdev->base + CICR0);
/* Restart the Capture Interface */
- CICR0 |= CICR0_ENB;
+ __raw_writel(cicr0 | CICR0_ENB, pcdev->base + CICR0);
goto out;
}
@@ -598,24 +704,43 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q,
sizeof(struct pxa_buffer), icd);
}
-static int mclk_get_divisor(struct pxa_camera_dev *pcdev)
+static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev)
{
- unsigned int mclk_10khz = pcdev->platform_mclk_10khz;
- unsigned long div;
+ unsigned long mclk = pcdev->mclk;
+ u32 div;
unsigned long lcdclk;
- lcdclk = clk_get_rate(pcdev->clk) / 10000;
+ lcdclk = clk_get_rate(pcdev->clk);
+ pcdev->ciclk = lcdclk;
- /* We verify platform_mclk_10khz != 0, so if anyone breaks it, here
- * they get a nice Oops */
- div = (lcdclk + 2 * mclk_10khz - 1) / (2 * mclk_10khz) - 1;
+ /* mclk <= ciclk / 4 (27.4.2) */
+ if (mclk > lcdclk / 4) {
+ mclk = lcdclk / 4;
+ dev_warn(pcdev->dev, "Limiting master clock to %lu\n", mclk);
+ }
+
+ /* We verify mclk != 0, so if anyone breaks it, here comes their Oops */
+ div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
- dev_dbg(pcdev->dev, "LCD clock %lukHz, target freq %dkHz, "
- "divisor %lu\n", lcdclk * 10, mclk_10khz * 10, div);
+ /* If we're not supplying MCLK, leave it at 0 */
+ if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+ pcdev->mclk = lcdclk / (2 * (div + 1));
+
+ dev_dbg(pcdev->dev, "LCD clock %luHz, target freq %luHz, "
+ "divisor %u\n", lcdclk, mclk, div);
return div;
}
+static void recalculate_fifo_timeout(struct pxa_camera_dev *pcdev,
+ unsigned long pclk)
+{
+ /* We want a timeout > 1 pixel time, not ">=" */
+ u32 ciclk_per_pixel = pcdev->ciclk / pclk + 1;
+
+ __raw_writel(ciclk_per_pixel, pcdev->base + CITOR);
+}
+
static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
{
struct pxacamera_platform_data *pdata = pcdev->pdata;
@@ -629,7 +754,8 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
pdata->init(pcdev->dev);
}
- CICR0 = 0x3FF; /* disable all interrupts */
+ /* disable all interrupts */
+ __raw_writel(0x3ff, pcdev->base + CICR0);
if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
cicr4 |= CICR4_PCLK_EN;
@@ -642,7 +768,14 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
if (pcdev->platform_flags & PXA_CAMERA_VSP)
cicr4 |= CICR4_VSP;
- CICR4 = mclk_get_divisor(pcdev) | cicr4;
+ __raw_writel(pcdev->mclk_divisor | cicr4, pcdev->base + CICR4);
+
+ if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+ /* Initialise the timeout under the assumption pclk = mclk */
+ recalculate_fifo_timeout(pcdev, pcdev->mclk);
+ else
+ /* "Safe default" - 13MHz */
+ recalculate_fifo_timeout(pcdev, 13000000);
clk_enable(pcdev->clk);
}
@@ -655,14 +788,15 @@ static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev)
static irqreturn_t pxa_camera_irq(int irq, void *data)
{
struct pxa_camera_dev *pcdev = data;
- unsigned int status = CISR;
+ unsigned long status, cicr0;
- dev_dbg(pcdev->dev, "Camera interrupt status 0x%x\n", status);
+ status = __raw_readl(pcdev->base + CISR);
+ dev_dbg(pcdev->dev, "Camera interrupt status 0x%lx\n", status);
if (!status)
return IRQ_NONE;
- CISR = status;
+ __raw_writel(status, pcdev->base + CISR);
if (status & CISR_EOF) {
int i;
@@ -671,22 +805,24 @@ static irqreturn_t pxa_camera_irq(int irq, void *data)
pcdev->active->dmas[i].sg_dma;
DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
}
- CICR0 |= CICR0_EOFM;
+ cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM;
+ __raw_writel(cicr0, pcdev->base + CICR0);
}
return IRQ_HANDLED;
}
-/* The following two functions absolutely depend on the fact, that
- * there can be only one camera on PXA quick capture interface */
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on PXA quick capture interface
+ * Called with .video_lock held
+ */
static int pxa_camera_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
int ret;
- mutex_lock(&camera_lock);
-
if (pcdev->icd) {
ret = -EBUSY;
goto ebusy;
@@ -702,11 +838,10 @@ static int pxa_camera_add_device(struct soc_camera_device *icd)
pcdev->icd = icd;
ebusy:
- mutex_unlock(&camera_lock);
-
return ret;
}
+/* Called with .video_lock held */
static void pxa_camera_remove_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
@@ -718,7 +853,7 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd)
icd->devnum);
/* disable capture, disable interrupts */
- CICR0 = 0x3ff;
+ __raw_writel(0x3ff, pcdev->base + CICR0);
/* Stop DMA engine */
DCSR(pcdev->dma_chans[0]) = 0;
@@ -765,6 +900,9 @@ static int test_platform_param(struct pxa_camera_dev *pcdev,
if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8))
return -EINVAL;
*flags |= SOCAM_DATAWIDTH_8;
+ break;
+ default:
+ return -EINVAL;
}
return 0;
@@ -772,11 +910,10 @@ static int test_platform_param(struct pxa_camera_dev *pcdev,
static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
{
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
unsigned long dw, bpp, bus_flags, camera_flags, common_flags;
- u32 cicr0, cicr1, cicr4 = 0;
+ u32 cicr0, cicr1, cicr2, cicr3, cicr4 = 0;
int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags);
if (ret < 0)
@@ -823,12 +960,10 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
* We fix bit-per-pixel equal to data-width... */
switch (common_flags & SOCAM_DATAWIDTH_MASK) {
case SOCAM_DATAWIDTH_10:
- icd->buswidth = 10;
dw = 4;
bpp = 0x40;
break;
case SOCAM_DATAWIDTH_9:
- icd->buswidth = 9;
dw = 3;
bpp = 0x20;
break;
@@ -836,7 +971,6 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
/* Actually it can only be 8 now,
* default is just to silence compiler warnings */
case SOCAM_DATAWIDTH_8:
- icd->buswidth = 8;
dw = 2;
bpp = 0;
}
@@ -852,9 +986,9 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
cicr4 |= CICR4_VSP;
- cicr0 = CICR0;
+ cicr0 = __raw_readl(pcdev->base + CICR0);
if (cicr0 & CICR0_ENB)
- CICR0 = cicr0 & ~CICR0_ENB;
+ __raw_writel(cicr0 & ~CICR0_ENB, pcdev->base + CICR0);
cicr1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw;
@@ -862,7 +996,17 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
case V4L2_PIX_FMT_YUV422P:
pcdev->channels = 3;
cicr1 |= CICR1_YCBCR_F;
+ /*
+ * Normally, pxa bus wants as input UYVY format. We allow all
+ * reorderings of the YUV422 format, as no processing is done,
+ * and the YUV stream is just passed through without any
+ * transformation. Note that UYVY is the only format that
+ * should be used if pxa framebuffer Overlay2 is used.
+ */
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
cicr1 |= CICR1_COLOR_SP_VAL(2);
break;
case V4L2_PIX_FMT_RGB555:
@@ -874,27 +1018,32 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
break;
}
- CICR1 = cicr1;
- CICR2 = 0;
- CICR3 = CICR3_LPF_VAL(icd->height - 1) |
+ cicr2 = 0;
+ cicr3 = CICR3_LPF_VAL(icd->height - 1) |
CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top));
- CICR4 = mclk_get_divisor(pcdev) | cicr4;
+ cicr4 |= pcdev->mclk_divisor;
+
+ __raw_writel(cicr1, pcdev->base + CICR1);
+ __raw_writel(cicr2, pcdev->base + CICR2);
+ __raw_writel(cicr3, pcdev->base + CICR3);
+ __raw_writel(cicr4, pcdev->base + CICR4);
/* CIF interrupts are not used, only DMA */
- CICR0 = (pcdev->platform_flags & PXA_CAMERA_MASTER ?
- CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP)) |
- CICR0_DMAEN | CICR0_IRQ_MASK | (cicr0 & CICR0_ENB);
+ cicr0 = (cicr0 & CICR0_ENB) | (pcdev->platform_flags & PXA_CAMERA_MASTER ?
+ CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP));
+ cicr0 |= CICR0_DMAEN | CICR0_IRQ_MASK;
+ __raw_writel(cicr0, pcdev->base + CICR0);
return 0;
}
-static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
+static int pxa_camera_try_bus_param(struct soc_camera_device *icd,
+ unsigned char buswidth)
{
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
unsigned long bus_flags, camera_flags;
- int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags);
+ int ret = test_platform_param(pcdev, buswidth, &bus_flags);
if (ret < 0)
return ret;
@@ -904,28 +1053,210 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL;
}
-static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect)
+static const struct soc_camera_data_format pxa_camera_formats[] = {
+ {
+ .name = "Planar YUV422 16 bit",
+ .depth = 16,
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+};
+
+static bool buswidth_supported(struct soc_camera_device *icd, int depth)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+
+ switch (depth) {
+ case 8:
+ return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8);
+ case 9:
+ return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9);
+ case 10:
+ return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10);
+ }
+ return false;
+}
+
+static int required_buswidth(const struct soc_camera_data_format *fmt)
{
- return icd->ops->set_fmt_cap(icd, pixfmt, rect);
+ switch (fmt->fourcc) {
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB555:
+ return 8;
+ default:
+ return fmt->depth;
+ }
}
-static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
+ struct soc_camera_format_xlate *xlate)
{
- /* limit to pxa hardware capabilities */
- if (f->fmt.pix.height < 32)
- f->fmt.pix.height = 32;
- if (f->fmt.pix.height > 2048)
- f->fmt.pix.height = 2048;
- if (f->fmt.pix.width < 48)
- f->fmt.pix.width = 48;
- if (f->fmt.pix.width > 2048)
- f->fmt.pix.width = 2048;
- f->fmt.pix.width &= ~0x01;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ int formats = 0, buswidth, ret;
+
+ buswidth = required_buswidth(icd->formats + idx);
+ if (!buswidth_supported(icd, buswidth))
+ return 0;
+
+ ret = pxa_camera_try_bus_param(icd, buswidth);
+ if (ret < 0)
+ return 0;
+
+ switch (icd->formats[idx].fourcc) {
+ case V4L2_PIX_FMT_UYVY:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &pxa_camera_formats[0];
+ xlate->cam_fmt = icd->formats + idx;
+ xlate->buswidth = buswidth;
+ xlate++;
+ dev_dbg(&ici->dev, "Providing format %s using %s\n",
+ pxa_camera_formats[0].name,
+ icd->formats[idx].name);
+ }
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB555:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = icd->formats + idx;
+ xlate->cam_fmt = icd->formats + idx;
+ xlate->buswidth = buswidth;
+ xlate++;
+ dev_dbg(&ici->dev, "Providing format %s packed\n",
+ icd->formats[idx].name);
+ }
+ break;
+ default:
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = icd->formats + idx;
+ xlate->cam_fmt = icd->formats + idx;
+ xlate->buswidth = icd->formats[idx].depth;
+ xlate++;
+ dev_dbg(&ici->dev,
+ "Providing format %s in pass-through mode\n",
+ icd->formats[idx].name);
+ }
+ }
+
+ return formats;
+}
+
+static int pxa_camera_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+ const struct soc_camera_data_format *host_fmt, *cam_fmt = NULL;
+ const struct soc_camera_format_xlate *xlate;
+ struct soc_camera_sense sense = {
+ .master_clock = pcdev->mclk,
+ .pixel_clock_max = pcdev->ciclk / 4,
+ };
+ int ret, buswidth;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ buswidth = xlate->buswidth;
+ host_fmt = xlate->host_fmt;
+ cam_fmt = xlate->cam_fmt;
+
+ /* If PCLK is used to latch data from the sensor, check sense */
+ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+ icd->sense = &sense;
+
+ switch (pixfmt) {
+ case 0: /* Only geometry change */
+ ret = icd->ops->set_fmt(icd, pixfmt, rect);
+ break;
+ default:
+ ret = icd->ops->set_fmt(icd, cam_fmt->fourcc, rect);
+ }
+
+ icd->sense = NULL;
+
+ if (ret < 0) {
+ dev_warn(&ici->dev, "Failed to configure for format %x\n",
+ pixfmt);
+ } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
+ if (sense.pixel_clock > sense.pixel_clock_max) {
+ dev_err(&ici->dev,
+ "pixel clock %lu set by the camera too high!",
+ sense.pixel_clock);
+ return -EIO;
+ }
+ recalculate_fifo_timeout(pcdev, sense.pixel_clock);
+ }
+
+ if (pixfmt && !ret) {
+ icd->buswidth = buswidth;
+ icd->current_fmt = host_fmt;
+ }
+
+ return ret;
+}
+
+static int pxa_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ __u32 pixfmt = pix->pixelformat;
+ enum v4l2_field field;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* limit to pxa hardware capabilities */
+ if (pix->height < 32)
+ pix->height = 32;
+ if (pix->height > 2048)
+ pix->height = 2048;
+ if (pix->width < 48)
+ pix->width = 48;
+ if (pix->width > 2048)
+ pix->width = 2048;
+ pix->width &= ~0x01;
+
+ pix->bytesperline = pix->width *
+ DIV_ROUND_UP(xlate->host_fmt->depth, 8);
+ pix->sizeimage = pix->height * pix->bytesperline;
+
+ /* camera has to see its format, but the user the original one */
+ pix->pixelformat = xlate->cam_fmt->fourcc;
/* limit to sensor capabilities */
- return icd->ops->try_fmt_cap(icd, f);
+ ret = icd->ops->try_fmt(icd, f);
+ pix->pixelformat = xlate->host_fmt->fourcc;
+
+ field = pix->field;
+
+ if (field == V4L2_FIELD_ANY) {
+ pix->field = V4L2_FIELD_NONE;
+ } else if (field != V4L2_FIELD_NONE) {
+ dev_err(&icd->dev, "Field type %d unsupported.\n", field);
+ return -EINVAL;
+ }
+
+ return ret;
}
static int pxa_camera_reqbufs(struct soc_camera_file *icf,
@@ -977,16 +1308,15 @@ static int pxa_camera_querycap(struct soc_camera_host *ici,
static int pxa_camera_suspend(struct soc_camera_device *icd, pm_message_t state)
{
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
int i = 0, ret = 0;
- pcdev->save_cicr[i++] = CICR0;
- pcdev->save_cicr[i++] = CICR1;
- pcdev->save_cicr[i++] = CICR2;
- pcdev->save_cicr[i++] = CICR3;
- pcdev->save_cicr[i++] = CICR4;
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR0);
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR1);
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR2);
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3);
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4);
if ((pcdev->icd) && (pcdev->icd->ops->suspend))
ret = pcdev->icd->ops->suspend(pcdev->icd, state);
@@ -996,8 +1326,7 @@ static int pxa_camera_suspend(struct soc_camera_device *icd, pm_message_t state)
static int pxa_camera_resume(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
int i = 0, ret = 0;
@@ -1005,23 +1334,27 @@ static int pxa_camera_resume(struct soc_camera_device *icd)
DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD;
DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD;
- CICR0 = pcdev->save_cicr[i++] & ~CICR0_ENB;
- CICR1 = pcdev->save_cicr[i++];
- CICR2 = pcdev->save_cicr[i++];
- CICR3 = pcdev->save_cicr[i++];
- CICR4 = pcdev->save_cicr[i++];
+ __raw_writel(pcdev->save_cicr[i++] & ~CICR0_ENB, pcdev->base + CICR0);
+ __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR1);
+ __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR2);
+ __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3);
+ __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4);
if ((pcdev->icd) && (pcdev->icd->ops->resume))
ret = pcdev->icd->ops->resume(pcdev->icd);
/* Restart frame capture if active buffer exists */
if (!ret && pcdev->active) {
+ unsigned long cifr, cicr0;
+
/* Reset the FIFOs */
- CIFR |= CIFR_RESET_F;
- /* Enable End-Of-Frame Interrupt */
- CICR0 &= ~CICR0_EOFM;
- /* Restart the Capture Interface */
- CICR0 |= CICR0_ENB;
+ cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
+ __raw_writel(cifr, pcdev->base + CIFR);
+
+ cicr0 = __raw_readl(pcdev->base + CICR0);
+ cicr0 &= ~CICR0_EOFM; /* Enable End-Of-Frame Interrupt */
+ cicr0 |= CICR0_ENB; /* Restart the Capture Interface */
+ __raw_writel(cicr0, pcdev->base + CICR0);
}
return ret;
@@ -1033,13 +1366,13 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
.remove = pxa_camera_remove_device,
.suspend = pxa_camera_suspend,
.resume = pxa_camera_resume,
- .set_fmt_cap = pxa_camera_set_fmt_cap,
- .try_fmt_cap = pxa_camera_try_fmt_cap,
+ .get_formats = pxa_camera_get_formats,
+ .set_fmt = pxa_camera_set_fmt,
+ .try_fmt = pxa_camera_try_fmt,
.init_videobuf = pxa_camera_init_videobuf,
.reqbufs = pxa_camera_reqbufs,
.poll = pxa_camera_poll,
.querycap = pxa_camera_querycap,
- .try_bus_param = pxa_camera_try_bus_param,
.set_bus_param = pxa_camera_set_bus_param,
};
@@ -1071,7 +1404,7 @@ static int pxa_camera_probe(struct platform_device *pdev)
goto exit;
}
- pcdev->clk = clk_get(&pdev->dev, "CAMCLK");
+ pcdev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(pcdev->clk)) {
err = PTR_ERR(pcdev->clk);
goto exit_kfree;
@@ -1090,14 +1423,17 @@ static int pxa_camera_probe(struct platform_device *pdev)
"data widths, using default 10 bit\n");
pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10;
}
- pcdev->platform_mclk_10khz = pcdev->pdata->mclk_10khz;
- if (!pcdev->platform_mclk_10khz) {
+ pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
+ if (!pcdev->mclk) {
dev_warn(&pdev->dev,
- "mclk_10khz == 0! Please, fix your platform data. "
+ "mclk == 0! Please, fix your platform data. "
"Using default 20MHz\n");
- pcdev->platform_mclk_10khz = 2000;
+ pcdev->mclk = 20000000;
}
+ pcdev->dev = &pdev->dev;
+ pcdev->mclk_divisor = mclk_get_divisor(pcdev);
+
INIT_LIST_HEAD(&pcdev->capture);
spin_lock_init(&pcdev->lock);
@@ -1117,7 +1453,6 @@ static int pxa_camera_probe(struct platform_device *pdev)
}
pcdev->irq = irq;
pcdev->base = base;
- pcdev->dev = &pdev->dev;
/* request dma */
err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
diff --git a/drivers/media/video/pxa_camera.h b/drivers/media/video/pxa_camera.h
new file mode 100644
index 000000000000..89cbfc9a35c5
--- /dev/null
+++ b/drivers/media/video/pxa_camera.h
@@ -0,0 +1,95 @@
+/* Camera Interface */
+#define CICR0 __REG(0x50000000)
+#define CICR1 __REG(0x50000004)
+#define CICR2 __REG(0x50000008)
+#define CICR3 __REG(0x5000000C)
+#define CICR4 __REG(0x50000010)
+#define CISR __REG(0x50000014)
+#define CIFR __REG(0x50000018)
+#define CITOR __REG(0x5000001C)
+#define CIBR0 __REG(0x50000028)
+#define CIBR1 __REG(0x50000030)
+#define CIBR2 __REG(0x50000038)
+
+#define CICR0_DMAEN (1 << 31) /* DMA request enable */
+#define CICR0_PAR_EN (1 << 30) /* Parity enable */
+#define CICR0_SL_CAP_EN (1 << 29) /* Capture enable for slave mode */
+#define CICR0_ENB (1 << 28) /* Camera interface enable */
+#define CICR0_DIS (1 << 27) /* Camera interface disable */
+#define CICR0_SIM (0x7 << 24) /* Sensor interface mode mask */
+#define CICR0_TOM (1 << 9) /* Time-out mask */
+#define CICR0_RDAVM (1 << 8) /* Receive-data-available mask */
+#define CICR0_FEM (1 << 7) /* FIFO-empty mask */
+#define CICR0_EOLM (1 << 6) /* End-of-line mask */
+#define CICR0_PERRM (1 << 5) /* Parity-error mask */
+#define CICR0_QDM (1 << 4) /* Quick-disable mask */
+#define CICR0_CDM (1 << 3) /* Disable-done mask */
+#define CICR0_SOFM (1 << 2) /* Start-of-frame mask */
+#define CICR0_EOFM (1 << 1) /* End-of-frame mask */
+#define CICR0_FOM (1 << 0) /* FIFO-overrun mask */
+
+#define CICR1_TBIT (1 << 31) /* Transparency bit */
+#define CICR1_RGBT_CONV (0x3 << 29) /* RGBT conversion mask */
+#define CICR1_PPL (0x7ff << 15) /* Pixels per line mask */
+#define CICR1_RGB_CONV (0x7 << 12) /* RGB conversion mask */
+#define CICR1_RGB_F (1 << 11) /* RGB format */
+#define CICR1_YCBCR_F (1 << 10) /* YCbCr format */
+#define CICR1_RGB_BPP (0x7 << 7) /* RGB bis per pixel mask */
+#define CICR1_RAW_BPP (0x3 << 5) /* Raw bis per pixel mask */
+#define CICR1_COLOR_SP (0x3 << 3) /* Color space mask */
+#define CICR1_DW (0x7 << 0) /* Data width mask */
+
+#define CICR2_BLW (0xff << 24) /* Beginning-of-line pixel clock
+ wait count mask */
+#define CICR2_ELW (0xff << 16) /* End-of-line pixel clock
+ wait count mask */
+#define CICR2_HSW (0x3f << 10) /* Horizontal sync pulse width mask */
+#define CICR2_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock
+ wait count mask */
+#define CICR2_FSW (0x7 << 0) /* Frame stabilization
+ wait count mask */
+
+#define CICR3_BFW (0xff << 24) /* Beginning-of-frame line clock
+ wait count mask */
+#define CICR3_EFW (0xff << 16) /* End-of-frame line clock
+ wait count mask */
+#define CICR3_VSW (0x3f << 10) /* Vertical sync pulse width mask */
+#define CICR3_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock
+ wait count mask */
+#define CICR3_LPF (0x7ff << 0) /* Lines per frame mask */
+
+#define CICR4_MCLK_DLY (0x3 << 24) /* MCLK Data Capture Delay mask */
+#define CICR4_PCLK_EN (1 << 23) /* Pixel clock enable */
+#define CICR4_PCP (1 << 22) /* Pixel clock polarity */
+#define CICR4_HSP (1 << 21) /* Horizontal sync polarity */
+#define CICR4_VSP (1 << 20) /* Vertical sync polarity */
+#define CICR4_MCLK_EN (1 << 19) /* MCLK enable */
+#define CICR4_FR_RATE (0x7 << 8) /* Frame rate mask */
+#define CICR4_DIV (0xff << 0) /* Clock divisor mask */
+
+#define CISR_FTO (1 << 15) /* FIFO time-out */
+#define CISR_RDAV_2 (1 << 14) /* Channel 2 receive data available */
+#define CISR_RDAV_1 (1 << 13) /* Channel 1 receive data available */
+#define CISR_RDAV_0 (1 << 12) /* Channel 0 receive data available */
+#define CISR_FEMPTY_2 (1 << 11) /* Channel 2 FIFO empty */
+#define CISR_FEMPTY_1 (1 << 10) /* Channel 1 FIFO empty */
+#define CISR_FEMPTY_0 (1 << 9) /* Channel 0 FIFO empty */
+#define CISR_EOL (1 << 8) /* End of line */
+#define CISR_PAR_ERR (1 << 7) /* Parity error */
+#define CISR_CQD (1 << 6) /* Camera interface quick disable */
+#define CISR_CDD (1 << 5) /* Camera interface disable done */
+#define CISR_SOF (1 << 4) /* Start of frame */
+#define CISR_EOF (1 << 3) /* End of frame */
+#define CISR_IFO_2 (1 << 2) /* FIFO overrun for Channel 2 */
+#define CISR_IFO_1 (1 << 1) /* FIFO overrun for Channel 1 */
+#define CISR_IFO_0 (1 << 0) /* FIFO overrun for Channel 0 */
+
+#define CIFR_FLVL2 (0x7f << 23) /* FIFO 2 level mask */
+#define CIFR_FLVL1 (0x7f << 16) /* FIFO 1 level mask */
+#define CIFR_FLVL0 (0xff << 8) /* FIFO 0 level mask */
+#define CIFR_THL_0 (0x3 << 4) /* Threshold Level for Channel 0 FIFO */
+#define CIFR_RESET_F (1 << 3) /* Reset input FIFOs */
+#define CIFR_FEN2 (1 << 2) /* FIFO enable for channel 2 */
+#define CIFR_FEN1 (1 << 1) /* FIFO enable for channel 1 */
+#define CIFR_FEN0 (1 << 0) /* FIFO enable for channel 0 */
+
diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c
index 4a21b8a6a709..f159441e9375 100644
--- a/drivers/media/video/saa5246a.c
+++ b/drivers/media/video/saa5246a.c
@@ -15,7 +15,7 @@
* <richard.guenther@student.uni-tuebingen.de>
*
* with changes by
- * Alan Cox <Alan.Cox@linux.org>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* and
*
@@ -804,8 +804,7 @@ static inline int saa5246a_stop_dau(struct saa5246a_device *t,
*
* Returns 0 if successful
*/
-static int do_saa5246a_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int do_saa5246a_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct saa5246a_device *t = video_drvdata(file);
@@ -953,7 +952,7 @@ static int saa5246a_ioctl(struct inode *inode, struct file *file,
cmd = vtx_fix_command(cmd);
mutex_lock(&t->lock);
- err = video_usercopy(inode, file, cmd, arg, do_saa5246a_ioctl);
+ err = video_usercopy(file, cmd, arg, do_saa5246a_ioctl);
mutex_unlock(&t->lock);
return err;
}
diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c
index 3bb959c25d9d..6ef3affb97f1 100644
--- a/drivers/media/video/saa5249.c
+++ b/drivers/media/video/saa5249.c
@@ -8,7 +8,7 @@
* you can add arbitary multiple teletext devices to Linux video4linux
* now (well 32 anyway).
*
- * Alan Cox <Alan.Cox@linux.org>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* The original driver was heavily modified to match the i2c interface
* It was truncated to use the WinTV boards, too.
@@ -190,8 +190,7 @@ static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf)
* Standard character-device-driver functions
*/
-static int do_saa5249_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int do_saa5249_ioctl(struct file *file, unsigned int cmd, void *arg)
{
static int virtual_mode = false;
struct saa5249_device *t = video_drvdata(file);
@@ -488,7 +487,7 @@ static int saa5249_ioctl(struct inode *inode, struct file *file,
cmd = vtx_fix_command(cmd);
mutex_lock(&t->lock);
- err = video_usercopy(inode,file,cmd,arg,do_saa5249_ioctl);
+ err = video_usercopy(file, cmd, arg, do_saa5249_ioctl);
mutex_unlock(&t->lock);
return err;
}
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index c8e9cb3db30a..22708ecdf1bb 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -44,7 +44,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include <media/saa7115.h>
@@ -70,6 +70,7 @@ static unsigned short normal_i2c[] = {
I2C_CLIENT_INSMOD;
struct saa711x_state {
+ struct v4l2_subdev sd;
v4l2_std_id std;
int input;
int output;
@@ -89,10 +90,17 @@ struct saa711x_state {
u8 apll;
};
+static inline struct saa711x_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa711x_state, sd);
+}
+
/* ----------------------------------------------------------------------- */
-static inline int saa711x_write(struct i2c_client *client, u8 reg, u8 value)
+static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_write_byte_data(client, reg, value);
}
@@ -128,9 +136,9 @@ static int saa711x_has_reg(const int id, const u8 reg)
return 1;
}
-static int saa711x_writeregs(struct i2c_client *client, const unsigned char *regs)
+static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
unsigned char reg, data;
while (*regs != 0x00) {
@@ -139,18 +147,20 @@ static int saa711x_writeregs(struct i2c_client *client, const unsigned char *reg
/* According with datasheets, reserved regs should be
filled with 0 - seems better not to touch on they */
- if (saa711x_has_reg(state->ident,reg)) {
- if (saa711x_write(client, reg, data) < 0)
+ if (saa711x_has_reg(state->ident, reg)) {
+ if (saa711x_write(sd, reg, data) < 0)
return -1;
} else {
- v4l_dbg(1, debug, client, "tried to access reserved reg 0x%02x\n", reg);
+ v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg);
}
}
return 0;
}
-static inline int saa711x_read(struct i2c_client *client, u8 reg)
+static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_read_byte_data(client, reg);
}
@@ -601,7 +611,7 @@ static int saa711x_odd_parity(u8 c)
return c & 1;
}
-static int saa711x_decode_vps(u8 * dst, u8 * p)
+static int saa711x_decode_vps(u8 *dst, u8 *p)
{
static const u8 biphase_tbl[] = {
0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
@@ -648,7 +658,7 @@ static int saa711x_decode_vps(u8 * dst, u8 * p)
return err & 0xf0;
}
-static int saa711x_decode_wss(u8 * p)
+static int saa711x_decode_wss(u8 *p)
{
static const int wss_bits[8] = {
0, 0, 0, 1, 0, 1, 1, 1
@@ -675,9 +685,9 @@ static int saa711x_decode_wss(u8 * p)
return wss;
}
-static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
+static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
u32 acpf;
u32 acni;
u32 hz;
@@ -685,10 +695,10 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
u8 acc = 0; /* reg 0x3a, audio clock control */
/* Checks for chips that don't have audio clock (saa7111, saa7113) */
- if (!saa711x_has_reg(state->ident,R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
+ if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
return 0;
- v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq);
+ v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq);
/* sanity check */
if (freq < 32000 || freq > 48000)
@@ -715,66 +725,66 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
if (state->apll)
acc |= 0x08;
- saa711x_write(client, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
- saa711x_write(client, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
- saa711x_write(client, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
+ saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
+ saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
+ saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
- saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
- saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
(acpf >> 8) & 0xff);
- saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
(acpf >> 16) & 0x03);
- saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
- saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
- saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
state->audclk_freq = freq;
return 0;
}
-static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value < 0 || ctrl->value > 255) {
- v4l_err(client, "invalid brightness setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
return -ERANGE;
}
state->bright = ctrl->value;
- saa711x_write(client, R_0A_LUMA_BRIGHT_CNTL, state->bright);
+ saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright);
break;
case V4L2_CID_CONTRAST:
if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid contrast setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
return -ERANGE;
}
state->contrast = ctrl->value;
- saa711x_write(client, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
+ saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
break;
case V4L2_CID_SATURATION:
if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid saturation setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
return -ERANGE;
}
state->sat = ctrl->value;
- saa711x_write(client, R_0C_CHROMA_SAT_CNTL, state->sat);
+ saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat);
break;
case V4L2_CID_HUE:
if (ctrl->value < -127 || ctrl->value > 127) {
- v4l_err(client, "invalid hue setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
return -ERANGE;
}
state->hue = ctrl->value;
- saa711x_write(client, R_0D_CHROMA_HUE_CNTL, state->hue);
+ saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue);
break;
default:
@@ -784,9 +794,9 @@ static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c
return 0;
}
-static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
@@ -808,16 +818,16 @@ static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *c
return 0;
}
-static int saa711x_set_size(struct i2c_client *client, int width, int height)
+static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
int HPSC, HFSC;
int VSCY;
int res;
int is_50hz = state->std & V4L2_STD_625_50;
int Vsrc = is_50hz ? 576 : 480;
- v4l_dbg(1, debug, client, "decoder set size to %ix%i\n",width,height);
+ v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height);
/* FIXME need better bounds checking here */
if ((width < 1) || (width > 1440))
@@ -825,7 +835,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
if ((height < 1) || (height > Vsrc))
return -EINVAL;
- if (!saa711x_has_reg(state->ident,R_D0_B_HORIZ_PRESCALING)) {
+ if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) {
/* Decoder only supports 720 columns and 480 or 576 lines */
if (width != 720)
return -EINVAL;
@@ -843,22 +853,22 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
/* Set output width/height */
/* width */
- saa711x_write(client, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
+ saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
(u8) (width & 0xff));
- saa711x_write(client, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
+ saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
(u8) ((width >> 8) & 0xff));
/* Vertical Scaling uses height/2 */
- res=height/2;
+ res = height / 2;
/* On 60Hz, it is using a higher Vertical Output Size */
if (!is_50hz)
res += (VRES_60HZ - 480) >> 1;
/* height */
- saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
+ saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
(u8) (res & 0xff));
- saa711x_write(client, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
+ saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
(u8) ((res >> 8) & 0xff));
/* Scaling settings */
@@ -869,54 +879,54 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
HFSC = (int)((1024 * 720) / (HPSC * width));
/* FIXME hardcodes to "Task B"
* write H prescaler integer */
- saa711x_write(client, R_D0_B_HORIZ_PRESCALING,
+ saa711x_write(sd, R_D0_B_HORIZ_PRESCALING,
(u8) (HPSC & 0x3f));
- v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
+ v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
/* write H fine-scaling (luminance) */
- saa711x_write(client, R_D8_B_HORIZ_LUMA_SCALING_INC,
+ saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC,
(u8) (HFSC & 0xff));
- saa711x_write(client, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
+ saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
(u8) ((HFSC >> 8) & 0xff));
/* write H fine-scaling (chrominance)
* must be lum/2, so i'll just bitshift :) */
- saa711x_write(client, R_DC_B_HORIZ_CHROMA_SCALING,
+ saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING,
(u8) ((HFSC >> 1) & 0xff));
- saa711x_write(client, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
+ saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
(u8) ((HFSC >> 9) & 0xff));
VSCY = (int)((1024 * Vsrc) / height);
- v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
+ v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
/* Correct Contrast and Luminance */
- saa711x_write(client, R_D5_B_LUMA_CONTRAST_CNTL,
+ saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL,
(u8) (64 * 1024 / VSCY));
- saa711x_write(client, R_D6_B_CHROMA_SATURATION_CNTL,
+ saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL,
(u8) (64 * 1024 / VSCY));
/* write V fine-scaling (luminance) */
- saa711x_write(client, R_E0_B_VERT_LUMA_SCALING_INC,
+ saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC,
(u8) (VSCY & 0xff));
- saa711x_write(client, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
+ saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
(u8) ((VSCY >> 8) & 0xff));
/* write V fine-scaling (chrominance) */
- saa711x_write(client, R_E2_B_VERT_CHROMA_SCALING_INC,
+ saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC,
(u8) (VSCY & 0xff));
- saa711x_write(client, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
+ saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
(u8) ((VSCY >> 8) & 0xff));
- saa711x_writeregs(client, saa7115_cfg_reset_scaler);
+ saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
/* Activates task "B" */
- saa711x_write(client, R_80_GLOBAL_CNTL_1,
- saa711x_read(client,R_80_GLOBAL_CNTL_1) | 0x20);
+ saa711x_write(sd, R_80_GLOBAL_CNTL_1,
+ saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20);
return 0;
}
-static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
+static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
/* Prevent unnecessary standard changes. During a standard
change the I-Port is temporarily disabled. Any devices
@@ -932,13 +942,13 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
// This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
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);
+ v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n");
+ saa711x_writeregs(sd, saa7115_cfg_60hz_video);
+ saa711x_set_size(sd, 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);
+ v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n");
+ saa711x_writeregs(sd, saa7115_cfg_50hz_video);
+ saa711x_set_size(sd, 720, 576);
}
/* Register 0E - Bits D6-D4 on NO-AUTO mode
@@ -952,7 +962,7 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
*/
if (state->ident == V4L2_IDENT_SAA7111 ||
state->ident == V4L2_IDENT_SAA7113) {
- u8 reg = saa711x_read(client, R_0E_CHROMA_CNTL_1) & 0x8f;
+ u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
if (std == V4L2_STD_PAL_M) {
reg |= 0x30;
@@ -965,87 +975,31 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
} else if (std & V4L2_STD_SECAM) {
reg |= 0x50;
}
- saa711x_write(client, R_0E_CHROMA_CNTL_1, reg);
+ saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg);
} else {
/* restart task B if needed */
- int taskb = saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10;
+ int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10;
if (taskb && state->ident == V4L2_IDENT_SAA7114) {
- saa711x_writeregs(client, saa7115_cfg_vbi_on);
+ saa711x_writeregs(sd, saa7115_cfg_vbi_on);
}
/* switch audio mode too! */
- saa711x_set_audio_clock_freq(client, state->audclk_freq);
- }
-}
-
-static v4l2_std_id saa711x_get_v4lstd(struct i2c_client *client)
-{
- struct saa711x_state *state = i2c_get_clientdata(client);
-
- return state->std;
-}
-
-static void saa711x_log_status(struct i2c_client *client)
-{
- struct saa711x_state *state = i2c_get_clientdata(client);
- int reg1e, reg1f;
- int signalOk;
- int vcr;
-
- v4l_info(client, "Audio frequency: %d Hz\n", state->audclk_freq);
- if (state->ident != V4L2_IDENT_SAA7115) {
- /* status for the saa7114 */
- reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
- signalOk = (reg1f & 0xc1) == 0x81;
- v4l_info(client, "Video signal: %s\n", signalOk ? "ok" : "bad");
- v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
- return;
- }
-
- /* status for the saa7115 */
- reg1e = saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC);
- reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
-
- signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
- vcr = !(reg1f & 0x10);
-
- if (state->input >= 6) {
- v4l_info(client, "Input: S-Video %d\n", state->input - 6);
- } else {
- v4l_info(client, "Input: Composite %d\n", state->input);
+ saa711x_s_clock_freq(sd, state->audclk_freq);
}
- v4l_info(client, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
- v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
-
- switch (reg1e & 0x03) {
- case 1:
- v4l_info(client, "Detected format: NTSC\n");
- break;
- case 2:
- v4l_info(client, "Detected format: PAL\n");
- break;
- case 3:
- v4l_info(client, "Detected format: SECAM\n");
- break;
- default:
- v4l_info(client, "Detected format: BW/No color\n");
- break;
- }
- v4l_info(client, "Width, Height: %d, %d\n", state->width, state->height);
}
/* setup the sliced VBI lcr registers according to the sliced VBI format */
-static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt)
+static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
int is_50hz = (state->std & V4L2_STD_625_50);
u8 lcr[24];
int i, x;
#if 1
/* saa7113/7114/7118 VBI support are experimental */
- if (!saa711x_has_reg(state->ident,R_41_LCR_BASE))
+ if (!saa711x_has_reg(state->ident, R_41_LCR_BASE))
return;
#else
@@ -1109,16 +1063,16 @@ static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo
/* write the lcr registers */
for (i = 2; i <= 23; i++) {
- saa711x_write(client, i - 2 + R_41_LCR_BASE, lcr[i]);
+ saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]);
}
/* enable/disable raw VBI capturing */
- saa711x_writeregs(client, fmt == NULL ?
+ saa711x_writeregs(sd, fmt == NULL ?
saa7115_cfg_vbi_on :
saa7115_cfg_vbi_off);
}
-static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
static u16 lcr2vbi[] = {
0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
@@ -1134,10 +1088,10 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
return -EINVAL;
memset(sliced, 0, sizeof(*sliced));
/* done if using raw VBI */
- if (saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10)
+ if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10)
return 0;
for (i = 2; i <= 23; i++) {
- u8 v = saa711x_read(client, i - 2 + R_41_LCR_BASE);
+ u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE);
sliced->service_lines[0][i] = lcr2vbi[v >> 4];
sliced->service_lines[1][i] = lcr2vbi[v & 0xf];
@@ -1147,20 +1101,20 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
return 0;
}
-static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int saa711x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
- saa711x_set_lcr(client, &fmt->fmt.sliced);
+ saa711x_set_lcr(sd, &fmt->fmt.sliced);
return 0;
}
if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- saa711x_set_lcr(client, NULL);
+ saa711x_set_lcr(sd, NULL);
return 0;
}
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- return saa711x_set_size(client,fmt->fmt.pix.width,fmt->fmt.pix.height);
+ return saa711x_set_size(sd, fmt->fmt.pix.width, fmt->fmt.pix.height);
}
/* Decode the sliced VBI data stream as created by the saa7115.
@@ -1169,13 +1123,12 @@ static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
The current implementation uses SAV/EAV codes and not the ancillary data
headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV
code. */
-static void saa711x_decode_vbi_line(struct i2c_client *client,
- struct v4l2_decode_vbi_line *vbi)
+static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
{
+ struct saa711x_state *state = to_state(sd);
static const char vbi_no_data_pattern[] = {
0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0
};
- struct saa711x_state *state = i2c_get_clientdata(client);
u8 *p = vbi->p;
u32 wss;
int id1, id2; /* the ID1 and ID2 bytes from the internal header */
@@ -1202,7 +1155,7 @@ static void saa711x_decode_vbi_line(struct i2c_client *client,
/* If the VBI slicer does not detect any signal it will fill up
the payload buffer with 0xa0 bytes. */
if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern)))
- return;
+ return 0;
/* decode payloads */
switch (id2) {
@@ -1211,275 +1164,352 @@ static void saa711x_decode_vbi_line(struct i2c_client *client,
break;
case 4:
if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1]))
- return;
+ return 0;
vbi->type = V4L2_SLICED_CAPTION_525;
break;
case 5:
wss = saa711x_decode_wss(p);
if (wss == -1)
- return;
+ return 0;
p[0] = wss & 0xff;
p[1] = wss >> 8;
vbi->type = V4L2_SLICED_WSS_625;
break;
case 7:
if (saa711x_decode_vps(p, p) != 0)
- return;
+ return 0;
vbi->type = V4L2_SLICED_VPS;
break;
default:
- return;
+ break;
}
+ return 0;
}
/* ============ SAA7115 AUDIO settings (end) ============= */
-static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
+ int status;
- /* ioctls to allow direct access to the saa7115 registers for testing */
- switch (cmd) {
- case VIDIOC_S_FMT:
- return saa711x_set_v4lfmt(client, (struct v4l2_format *)arg);
+ if (state->radio)
+ return 0;
+ status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
- case VIDIOC_G_FMT:
- return saa711x_get_v4lfmt(client, (struct v4l2_format *)arg);
+ v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status);
+ vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
+ return 0;
+}
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- return saa711x_set_audio_clock_freq(client, *(u32 *)arg);
+static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill_std(qc);
+ default:
+ return -EINVAL;
+ }
+}
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = arg;
- int status;
+static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa711x_state *state = to_state(sd);
- if (state->radio)
- break;
- status = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
+ state->radio = 0;
+ saa711x_set_v4lstd(sd, std);
+ return 0;
+}
- v4l_dbg(1, debug, client, "status: 0x%02x\n", status);
- vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
- break;
- }
+static int saa711x_s_radio(struct v4l2_subdev *sd)
+{
+ struct saa711x_state *state = to_state(sd);
- case VIDIOC_LOG_STATUS:
- saa711x_log_status(client);
- break;
+ state->radio = 1;
+ return 0;
+}
- case VIDIOC_G_CTRL:
- return saa711x_get_v4lctrl(client, (struct v4l2_control *)arg);
+static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct saa711x_state *state = to_state(sd);
+ u32 input = route->input;
+ u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0;
+
+ v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", route->input, route->output);
+ /* saa7111/3 does not have these inputs */
+ if ((state->ident == V4L2_IDENT_SAA7113 ||
+ state->ident == V4L2_IDENT_SAA7111) &&
+ (route->input == SAA7115_COMPOSITE4 ||
+ route->input == SAA7115_COMPOSITE5)) {
+ return -EINVAL;
+ }
+ if (route->input > SAA7115_SVIDEO3)
+ return -EINVAL;
+ if (route->output > SAA7115_IPORT_ON)
+ return -EINVAL;
+ if (state->input == route->input && state->output == route->output)
+ return 0;
+ v4l2_dbg(1, debug, sd, "now setting %s input %s output\n",
+ (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite",
+ (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
+ state->input = route->input;
+
+ /* saa7111 has slightly different input numbering */
+ if (state->ident == V4L2_IDENT_SAA7111) {
+ if (input >= SAA7115_COMPOSITE4)
+ input -= 2;
+ /* saa7111 specific */
+ saa711x_write(sd, R_10_CHROMA_CNTL_2,
+ (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) |
+ ((route->output & 0xc0) ^ 0x40));
+ saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL,
+ (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
+ ((route->output & 2) ? 0x0a : 0));
+ }
- case VIDIOC_S_CTRL:
- return saa711x_set_v4lctrl(client, (struct v4l2_control *)arg);
+ /* select mode */
+ saa711x_write(sd, R_02_INPUT_CNTL_1,
+ (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) |
+ input);
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
+ /* bypass chrominance trap for S-Video modes */
+ saa711x_write(sd, R_09_LUMA_CNTL,
+ (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) |
+ (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill_std(qc);
- default:
- return -EINVAL;
- }
+ state->output = route->output;
+ if (state->ident == V4L2_IDENT_SAA7114 ||
+ state->ident == V4L2_IDENT_SAA7115) {
+ saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
+ (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
+ (state->output & 0x01));
}
+ return 0;
+}
- case VIDIOC_G_STD:
- *(v4l2_std_id *)arg = saa711x_get_v4lstd(client);
- break;
+static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val)
+{
+ struct saa711x_state *state = to_state(sd);
- case VIDIOC_S_STD:
- state->radio = 0;
- saa711x_set_v4lstd(client, *(v4l2_std_id *)arg);
- break;
+ if (state->ident != V4L2_IDENT_SAA7111)
+ return -EINVAL;
+ saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) |
+ (val ? 0x80 : 0));
+ return 0;
+}
- case AUDC_SET_RADIO:
- state->radio = 1;
- break;
+static int saa711x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa711x_state *state = to_state(sd);
- case VIDIOC_INT_G_VIDEO_ROUTING:
- {
- struct v4l2_routing *route = arg;
+ v4l2_dbg(1, debug, sd, "%s output\n",
+ enable ? "enable" : "disable");
- route->input = state->input;
- route->output = state->output;
- break;
+ if (state->enable != enable) {
+ state->enable = enable;
+ saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED,
+ state->enable);
}
+ return 0;
+}
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- struct v4l2_routing *route = arg;
- u32 input = route->input;
- u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0;
-
- v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output);
- /* saa7111/3 does not have these inputs */
- if ((state->ident == V4L2_IDENT_SAA7113 ||
- state->ident == V4L2_IDENT_SAA7111) &&
- (route->input == SAA7115_COMPOSITE4 ||
- route->input == SAA7115_COMPOSITE5)) {
- return -EINVAL;
- }
- if (route->input > SAA7115_SVIDEO3)
- return -EINVAL;
- if (route->output > SAA7115_IPORT_ON)
- return -EINVAL;
- if (state->input == route->input && state->output == route->output)
- break;
- v4l_dbg(1, debug, client, "now setting %s input %s output\n",
- (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
- state->input = route->input;
-
- /* saa7111 has slightly different input numbering */
- if (state->ident == V4L2_IDENT_SAA7111) {
- if (input >= SAA7115_COMPOSITE4)
- input -= 2;
- /* saa7111 specific */
- saa711x_write(client, R_10_CHROMA_CNTL_2,
- (saa711x_read(client, R_10_CHROMA_CNTL_2) & 0x3f) |
- ((route->output & 0xc0) ^ 0x40));
- saa711x_write(client, R_13_RT_X_PORT_OUT_CNTL,
- (saa711x_read(client, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
- ((route->output & 2) ? 0x0a : 0));
- }
+static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq)
+{
+ struct saa711x_state *state = to_state(sd);
- /* select mode */
- saa711x_write(client, R_02_INPUT_CNTL_1,
- (saa711x_read(client, R_02_INPUT_CNTL_1) & mask) |
- input);
+ if (freq->freq != SAA7115_FREQ_32_11_MHZ &&
+ freq->freq != SAA7115_FREQ_24_576_MHZ)
+ return -EINVAL;
+ state->crystal_freq = freq->freq;
+ state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
+ state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
+ state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
+ saa711x_s_clock_freq(sd, state->audclk_freq);
+ return 0;
+}
- /* bypass chrominance trap for S-Video modes */
- saa711x_write(client, R_09_LUMA_CNTL,
- (saa711x_read(client, R_09_LUMA_CNTL) & 0x7f) |
- (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
+static int saa711x_reset(struct v4l2_subdev *sd, u32 val)
+{
+ v4l2_dbg(1, debug, sd, "decoder RESET\n");
+ saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
+ return 0;
+}
- state->output = route->output;
- if (state->ident == V4L2_IDENT_SAA7114 ||
- state->ident == V4L2_IDENT_SAA7115) {
- saa711x_write(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
- (saa711x_read(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
- (state->output & 0x01));
+static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data)
+{
+ /* Note: the internal field ID is inverted for NTSC,
+ so data->field 0 maps to the saa7115 even field,
+ whereas for PAL it maps to the saa7115 odd field. */
+ switch (data->id) {
+ case V4L2_SLICED_WSS_625:
+ if (saa711x_read(sd, 0x6b) & 0xc0)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x6c);
+ data->data[1] = saa711x_read(sd, 0x6d);
+ return 0;
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0) {
+ /* CC */
+ if (saa711x_read(sd, 0x66) & 0x30)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x69);
+ data->data[1] = saa711x_read(sd, 0x6a);
+ return 0;
}
- break;
+ /* XDS */
+ if (saa711x_read(sd, 0x66) & 0xc0)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x67);
+ data->data[1] = saa711x_read(sd, 0x68);
+ return 0;
+ default:
+ return -EINVAL;
}
+}
- case VIDIOC_STREAMON:
- case VIDIOC_STREAMOFF:
- v4l_dbg(1, debug, client, "%s output\n",
- (cmd == VIDIOC_STREAMON) ? "enable" : "disable");
-
- if (state->enable != (cmd == VIDIOC_STREAMON)) {
- state->enable = (cmd == VIDIOC_STREAMON);
- saa711x_write(client,
- R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED,
- state->enable);
- }
- break;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_S_CRYSTAL_FREQ:
- {
- struct v4l2_crystal_freq *freq = arg;
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa711x_read(sd, reg->reg & 0xff);
+ return 0;
+}
- if (freq->freq != SAA7115_FREQ_32_11_MHZ &&
- freq->freq != SAA7115_FREQ_24_576_MHZ)
- return -EINVAL;
- state->crystal_freq = freq->freq;
- state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
- state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
- state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
- saa711x_set_audio_clock_freq(client, state->audclk_freq);
- break;
- }
+static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_DECODE_VBI_LINE:
- saa711x_decode_vbi_line(client, arg);
- break;
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
- case VIDIOC_INT_RESET:
- v4l_dbg(1, debug, client, "decoder RESET\n");
- saa711x_writeregs(client, saa7115_cfg_reset_scaler);
- break;
+static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct saa711x_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_S_GPIO:
- if (state->ident != V4L2_IDENT_SAA7111)
- return -EINVAL;
- saa711x_write(client, 0x11, (saa711x_read(client, 0x11) & 0x7f) |
- (*(u32 *)arg ? 0x80 : 0));
- break;
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
+}
- case VIDIOC_INT_G_VBI_DATA:
- {
- struct v4l2_sliced_vbi_data *data = arg;
+static int saa711x_log_status(struct v4l2_subdev *sd)
+{
+ struct saa711x_state *state = to_state(sd);
+ int reg1e, reg1f;
+ int signalOk;
+ int vcr;
- /* Note: the internal field ID is inverted for NTSC,
- so data->field 0 maps to the saa7115 even field,
- whereas for PAL it maps to the saa7115 odd field. */
- switch (data->id) {
- case V4L2_SLICED_WSS_625:
- if (saa711x_read(client, 0x6b) & 0xc0)
- return -EIO;
- data->data[0] = saa711x_read(client, 0x6c);
- data->data[1] = saa711x_read(client, 0x6d);
- return 0;
- case V4L2_SLICED_CAPTION_525:
- if (data->field == 0) {
- /* CC */
- if (saa711x_read(client, 0x66) & 0x30)
- return -EIO;
- data->data[0] = saa711x_read(client, 0x69);
- data->data[1] = saa711x_read(client, 0x6a);
- return 0;
- }
- /* XDS */
- if (saa711x_read(client, 0x66) & 0xc0)
- return -EIO;
- data->data[0] = saa711x_read(client, 0x67);
- data->data[1] = saa711x_read(client, 0x68);
- return 0;
- default:
- return -EINVAL;
- }
- break;
+ v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq);
+ if (state->ident != V4L2_IDENT_SAA7115) {
+ /* status for the saa7114 */
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ signalOk = (reg1f & 0xc1) == 0x81;
+ v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad");
+ v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
+ return 0;
}
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
+ /* status for the saa7115 */
+ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = saa711x_read(client, reg->reg & 0xff);
- else
- saa711x_write(client, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
-#endif
+ signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
+ vcr = !(reg1f & 0x10);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0);
+ if (state->input >= 6)
+ v4l2_info(sd, "Input: S-Video %d\n", state->input - 6);
+ else
+ v4l2_info(sd, "Input: Composite %d\n", state->input);
+ v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
+ v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
+ switch (reg1e & 0x03) {
+ case 1:
+ v4l2_info(sd, "Detected format: NTSC\n");
+ break;
+ case 2:
+ v4l2_info(sd, "Detected format: PAL\n");
+ break;
+ case 3:
+ v4l2_info(sd, "Detected format: SECAM\n");
+ break;
default:
- return -EINVAL;
+ v4l2_info(sd, "Detected format: BW/No color\n");
+ break;
}
-
+ v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height);
return 0;
}
+static int saa711x_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa711x_core_ops = {
+ .log_status = saa711x_log_status,
+ .g_chip_ident = saa711x_g_chip_ident,
+ .g_ctrl = saa711x_g_ctrl,
+ .s_ctrl = saa711x_s_ctrl,
+ .queryctrl = saa711x_queryctrl,
+ .reset = saa711x_reset,
+ .s_gpio = saa711x_s_gpio,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa711x_g_register,
+ .s_register = saa711x_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = {
+ .s_std = saa711x_s_std,
+ .s_radio = saa711x_s_radio,
+ .g_tuner = saa711x_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops saa711x_audio_ops = {
+ .s_clock_freq = saa711x_s_clock_freq,
+};
+
+static const struct v4l2_subdev_video_ops saa711x_video_ops = {
+ .s_routing = saa711x_s_routing,
+ .s_crystal_freq = saa711x_s_crystal_freq,
+ .g_fmt = saa711x_g_fmt,
+ .s_fmt = saa711x_s_fmt,
+ .g_vbi_data = saa711x_g_vbi_data,
+ .decode_vbi_line = saa711x_decode_vbi_line,
+ .s_stream = saa711x_s_stream,
+};
+
+static const struct v4l2_subdev_ops saa711x_ops = {
+ .core = &saa711x_core_ops,
+ .tuner = &saa711x_tuner_ops,
+ .audio = &saa711x_audio_ops,
+ .video = &saa711x_video_ops,
+};
+
/* ----------------------------------------------------------------------- */
-static int saa7115_probe(struct i2c_client *client,
+static int saa711x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct saa711x_state *state;
+ struct v4l2_subdev *sd;
int i;
char name[17];
char chip_id;
@@ -1490,8 +1520,8 @@ static int saa7115_probe(struct i2c_client *client,
return -EIO;
for (i = 0; i < 0x0f; i++) {
- saa711x_write(client, 0, i);
- name[i] = (saa711x_read(client, 0) & 0x0f) + '0';
+ i2c_smbus_write_byte_data(client, 0, i);
+ name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0';
if (name[i] > '9')
name[i] += 'a' - '9' - 1;
}
@@ -1518,7 +1548,8 @@ static int saa7115_probe(struct i2c_client *client,
state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
- i2c_set_clientdata(client, state);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
state->input = -1;
state->output = SAA7115_IPORT_ON;
state->enable = 1;
@@ -1545,41 +1576,45 @@ static int saa7115_probe(struct i2c_client *client,
break;
default:
state->ident = V4L2_IDENT_SAA7111;
- v4l_info(client, "WARNING: Chip is not known - Falling back to saa7111\n");
+ v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
}
state->audclk_freq = 48000;
- v4l_dbg(1, debug, client, "writing init values\n");
+ v4l2_dbg(1, debug, sd, "writing init values\n");
/* init to 60hz/48khz */
state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
switch (state->ident) {
case V4L2_IDENT_SAA7111:
- saa711x_writeregs(client, saa7111_init);
+ saa711x_writeregs(sd, saa7111_init);
break;
case V4L2_IDENT_SAA7113:
- saa711x_writeregs(client, saa7113_init);
+ saa711x_writeregs(sd, saa7113_init);
break;
default:
state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
- saa711x_writeregs(client, saa7115_init_auto_input);
+ saa711x_writeregs(sd, saa7115_init_auto_input);
}
if (state->ident != V4L2_IDENT_SAA7111)
- saa711x_writeregs(client, saa7115_init_misc);
- saa711x_set_v4lstd(client, V4L2_STD_NTSC);
+ saa711x_writeregs(sd, saa7115_init_misc);
+ saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
- v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n",
- saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC), saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC));
+ v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
+ saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
+ saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC));
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7115_remove(struct i2c_client *client)
+static int saa711x_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
@@ -1597,9 +1632,9 @@ MODULE_DEVICE_TABLE(i2c, saa7115_id);
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "saa7115",
.driverid = I2C_DRIVERID_SAA711X,
- .command = saa7115_command,
- .probe = saa7115_probe,
- .remove = saa7115_remove,
+ .command = saa711x_command,
+ .probe = saa711x_probe,
+ .remove = saa711x_remove,
.legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
.id_table = saa7115_id,
};
diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
index cc02fb18efa7..bfc85654795e 100644
--- a/drivers/media/video/saa7127.c
+++ b/drivers/media/video/saa7127.c
@@ -53,7 +53,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/saa7127.h>
@@ -231,6 +231,7 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = {
*/
struct saa7127_state {
+ struct v4l2_subdev sd;
v4l2_std_id std;
u32 ident;
enum saa7127_input_type input_type;
@@ -250,6 +251,11 @@ struct saa7127_state {
u8 reg_61;
};
+static inline struct saa7127_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa7127_state, sd);
+}
+
static const char * const output_strs[] =
{
"S-Video + Composite",
@@ -281,32 +287,35 @@ static const char * const wss_strs[] = {
/* ----------------------------------------------------------------------- */
-static int saa7127_read(struct i2c_client *client, u8 reg)
+static int saa7127_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_read_byte_data(client, reg);
}
/* ----------------------------------------------------------------------- */
-static int saa7127_write(struct i2c_client *client, u8 reg, u8 val)
+static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
for (i = 0; i < 3; i++) {
if (i2c_smbus_write_byte_data(client, reg, val) == 0)
return 0;
}
- v4l_err(client, "I2C Write Problem\n");
+ v4l2_err(sd, "I2C Write Problem\n");
return -1;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_write_inittab(struct i2c_client *client,
+static int saa7127_write_inittab(struct v4l2_subdev *sd,
const struct i2c_reg_value *regs)
{
while (regs->reg != 0) {
- saa7127_write(client, regs->reg, regs->value);
+ saa7127_write(sd, regs->reg, regs->value);
regs++;
}
return 0;
@@ -314,16 +323,16 @@ static int saa7127_write_inittab(struct i2c_client *client,
/* ----------------------------------------------------------------------- */
-static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
int enable = (data->line != 0);
if (enable && (data->field != 0 || data->line != 16))
return -EINVAL;
if (state->vps_enable != enable) {
- v4l_dbg(1, debug, client, "Turn VPS Signal %s\n", enable ? "on" : "off");
- saa7127_write(client, 0x54, enable << 7);
+ v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off");
+ saa7127_write(sd, 0x54, enable << 7);
state->vps_enable = enable;
}
if (!enable)
@@ -334,91 +343,91 @@ static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_dat
state->vps_data[2] = data->data[9];
state->vps_data[3] = data->data[10];
state->vps_data[4] = data->data[11];
- v4l_dbg(1, debug, client, "Set VPS data %02x %02x %02x %02x %02x\n",
+ v4l2_dbg(1, debug, sd, "Set VPS data %02x %02x %02x %02x %02x\n",
state->vps_data[0], state->vps_data[1],
state->vps_data[2], state->vps_data[3],
state->vps_data[4]);
- saa7127_write(client, 0x55, state->vps_data[0]);
- saa7127_write(client, 0x56, state->vps_data[1]);
- saa7127_write(client, 0x57, state->vps_data[2]);
- saa7127_write(client, 0x58, state->vps_data[3]);
- saa7127_write(client, 0x59, state->vps_data[4]);
+ saa7127_write(sd, 0x55, state->vps_data[0]);
+ saa7127_write(sd, 0x56, state->vps_data[1]);
+ saa7127_write(sd, 0x57, state->vps_data[2]);
+ saa7127_write(sd, 0x58, state->vps_data[3]);
+ saa7127_write(sd, 0x59, state->vps_data[4]);
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_cc(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
u16 cc = data->data[1] << 8 | data->data[0];
int enable = (data->line != 0);
if (enable && (data->field != 0 || data->line != 21))
return -EINVAL;
if (state->cc_enable != enable) {
- v4l_dbg(1, debug, client,
+ v4l2_dbg(1, debug, sd,
"Turn CC %s\n", enable ? "on" : "off");
- saa7127_write(client, SAA7127_REG_CLOSED_CAPTION,
+ saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION,
(state->xds_enable << 7) | (enable << 6) | 0x11);
state->cc_enable = enable;
}
if (!enable)
return 0;
- v4l_dbg(2, debug, client, "CC data: %04x\n", cc);
- saa7127_write(client, SAA7127_REG_LINE_21_ODD_0, cc & 0xff);
- saa7127_write(client, SAA7127_REG_LINE_21_ODD_1, cc >> 8);
+ v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc);
+ saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff);
+ saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8);
state->cc_data = cc;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_xds(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
u16 xds = data->data[1] << 8 | data->data[0];
int enable = (data->line != 0);
if (enable && (data->field != 1 || data->line != 21))
return -EINVAL;
if (state->xds_enable != enable) {
- v4l_dbg(1, debug, client, "Turn XDS %s\n", enable ? "on" : "off");
- saa7127_write(client, SAA7127_REG_CLOSED_CAPTION,
+ v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off");
+ saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION,
(enable << 7) | (state->cc_enable << 6) | 0x11);
state->xds_enable = enable;
}
if (!enable)
return 0;
- v4l_dbg(2, debug, client, "XDS data: %04x\n", xds);
- saa7127_write(client, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff);
- saa7127_write(client, SAA7127_REG_LINE_21_EVEN_1, xds >> 8);
+ v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds);
+ saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff);
+ saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8);
state->xds_data = xds;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
int enable = (data->line != 0);
if (enable && (data->field != 0 || data->line != 23))
return -EINVAL;
if (state->wss_enable != enable) {
- v4l_dbg(1, debug, client, "Turn WSS %s\n", enable ? "on" : "off");
- saa7127_write(client, 0x27, enable << 7);
+ v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off");
+ saa7127_write(sd, 0x27, enable << 7);
state->wss_enable = enable;
}
if (!enable)
return 0;
- saa7127_write(client, 0x26, data->data[0]);
- saa7127_write(client, 0x27, 0x80 | (data->data[1] & 0x3f));
- v4l_dbg(1, debug, client,
+ saa7127_write(sd, 0x26, data->data[0]);
+ saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f));
+ v4l2_dbg(1, debug, sd,
"WSS mode: %s\n", wss_strs[data->data[0] & 0xf]);
state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0];
return 0;
@@ -426,18 +435,18 @@ static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_dat
/* ----------------------------------------------------------------------- */
-static int saa7127_set_video_enable(struct i2c_client *client, int enable)
+static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
if (enable) {
- v4l_dbg(1, debug, client, "Enable Video Output\n");
- saa7127_write(client, 0x2d, state->reg_2d);
- saa7127_write(client, 0x61, state->reg_61);
+ v4l2_dbg(1, debug, sd, "Enable Video Output\n");
+ saa7127_write(sd, 0x2d, state->reg_2d);
+ saa7127_write(sd, 0x61, state->reg_61);
} else {
- v4l_dbg(1, debug, client, "Disable Video Output\n");
- saa7127_write(client, 0x2d, (state->reg_2d & 0xf0));
- saa7127_write(client, 0x61, (state->reg_61 | 0xc0));
+ v4l2_dbg(1, debug, sd, "Disable Video Output\n");
+ saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0));
+ saa7127_write(sd, 0x61, (state->reg_61 | 0xc0));
}
state->video_enable = enable;
return 0;
@@ -445,32 +454,32 @@ static int saa7127_set_video_enable(struct i2c_client *client, int enable)
/* ----------------------------------------------------------------------- */
-static int saa7127_set_std(struct i2c_client *client, v4l2_std_id std)
+static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
const struct i2c_reg_value *inittab;
if (std & V4L2_STD_525_60) {
- v4l_dbg(1, debug, client, "Selecting 60 Hz video Standard\n");
+ v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n");
inittab = saa7127_init_config_60hz;
state->reg_61 = SAA7127_60HZ_DAC_CONTROL;
} else {
- v4l_dbg(1, debug, client, "Selecting 50 Hz video Standard\n");
+ v4l2_dbg(1, debug, sd, "Selecting 50 Hz video Standard\n");
inittab = saa7127_init_config_50hz;
state->reg_61 = SAA7127_50HZ_DAC_CONTROL;
}
/* Write Table */
- saa7127_write_inittab(client, inittab);
+ saa7127_write_inittab(sd, inittab);
state->std = std;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_output_type(struct i2c_client *client, int output)
+static int saa7127_set_output_type(struct v4l2_subdev *sd, int output)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
switch (output) {
case SAA7127_OUTPUT_TYPE_RGB:
@@ -506,165 +515,195 @@ static int saa7127_set_output_type(struct i2c_client *client, int output)
default:
return -EINVAL;
}
- v4l_dbg(1, debug, client,
+ v4l2_dbg(1, debug, sd,
"Selecting %s output type\n", output_strs[output]);
/* Configure Encoder */
- saa7127_write(client, 0x2d, state->reg_2d);
- saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb);
+ saa7127_write(sd, 0x2d, state->reg_2d);
+ saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb);
state->output_type = output;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_input_type(struct i2c_client *client, int input)
+static int saa7127_set_input_type(struct v4l2_subdev *sd, int input)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
switch (input) {
case SAA7127_INPUT_TYPE_NORMAL: /* avia */
- v4l_dbg(1, debug, client, "Selecting Normal Encoder Input\n");
+ v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n");
state->reg_3a_cb = 0;
break;
case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */
- v4l_dbg(1, debug, client, "Selecting Color Bar generator\n");
+ v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n");
state->reg_3a_cb = 0x80;
break;
default:
return -EINVAL;
}
- saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb);
+ saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb);
state->input_type = input;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
- struct v4l2_format *fmt = arg;
- struct v4l2_routing *route = arg;
-
- switch (cmd) {
- case VIDIOC_INT_S_STD_OUTPUT:
- if (state->std == *(v4l2_std_id *)arg)
- break;
- return saa7127_set_std(client, *(v4l2_std_id *)arg);
-
- case VIDIOC_INT_G_STD_OUTPUT:
- *(v4l2_std_id *)arg = state->std;
- break;
+ struct saa7127_state *state = to_state(sd);
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = state->input_type;
- route->output = state->output_type;
- break;
+ if (state->std == std)
+ return 0;
+ return saa7127_set_std(sd, std);
+}
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- int rc = 0;
+static int saa7127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct saa7127_state *state = to_state(sd);
+ int rc = 0;
+
+ if (state->input_type != route->input)
+ rc = saa7127_set_input_type(sd, route->input);
+ if (rc == 0 && state->output_type != route->output)
+ rc = saa7127_set_output_type(sd, route->output);
+ return rc;
+}
- if (state->input_type != route->input)
- rc = saa7127_set_input_type(client, route->input);
- if (rc == 0 && state->output_type != route->output)
- rc = saa7127_set_output_type(client, route->output);
- return rc;
- }
+static int saa7127_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa7127_state *state = to_state(sd);
- case VIDIOC_STREAMON:
- case VIDIOC_STREAMOFF:
- if (state->video_enable == (cmd == VIDIOC_STREAMON))
- break;
- return saa7127_set_video_enable(client, cmd == VIDIOC_STREAMON);
-
- case VIDIOC_G_FMT:
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
-
- memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced));
- if (state->vps_enable)
- fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS;
- if (state->wss_enable)
- fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
- if (state->cc_enable) {
- fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
- fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
- }
- fmt->fmt.sliced.service_set =
- (state->vps_enable ? V4L2_SLICED_VPS : 0) |
- (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) |
- (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0);
- break;
+ if (state->video_enable == enable)
+ return 0;
+ return saa7127_set_video_enable(sd, enable);
+}
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz");
- v4l_info(client, "Input: %s\n", state->input_type ? "color bars" : "normal");
- v4l_info(client, "Output: %s\n", state->video_enable ?
- output_strs[state->output_type] : "disabled");
- v4l_info(client, "WSS: %s\n", state->wss_enable ?
- wss_strs[state->wss_mode] : "disabled");
- v4l_info(client, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled");
- v4l_info(client, "CC: %s\n", state->cc_enable ? "enabled" : "disabled");
- break;
+static int saa7127_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct saa7127_state *state = to_state(sd);
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client,
- reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = saa7127_read(client, reg->reg & 0xff);
- else
- saa7127_write(client, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
-#endif
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
- case VIDIOC_INT_S_VBI_DATA:
- {
- struct v4l2_sliced_vbi_data *data = arg;
-
- switch (data->id) {
- case V4L2_SLICED_WSS_625:
- return saa7127_set_wss(client, data);
- case V4L2_SLICED_VPS:
- return saa7127_set_vps(client, data);
- case V4L2_SLICED_CAPTION_525:
- if (data->field == 0)
- return saa7127_set_cc(client, data);
- return saa7127_set_xds(client, data);
- default:
- return -EINVAL;
- }
- break;
+ memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced));
+ if (state->vps_enable)
+ fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS;
+ if (state->wss_enable)
+ fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+ if (state->cc_enable) {
+ fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
}
+ fmt->fmt.sliced.service_set =
+ (state->vps_enable ? V4L2_SLICED_VPS : 0) |
+ (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) |
+ (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0);
+ return 0;
+}
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0);
-
+static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ switch (data->id) {
+ case V4L2_SLICED_WSS_625:
+ return saa7127_set_wss(sd, data);
+ case V4L2_SLICED_VPS:
+ return saa7127_set_vps(sd, data);
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0)
+ return saa7127_set_cc(sd, data);
+ return saa7127_set_xds(sd, data);
default:
return -EINVAL;
}
return 0;
}
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa7127_read(sd, reg->reg & 0xff);
+ return 0;
+}
+
+static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct saa7127_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
+}
+
+static int saa7127_log_status(struct v4l2_subdev *sd)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz");
+ v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal");
+ v4l2_info(sd, "Output: %s\n", state->video_enable ?
+ output_strs[state->output_type] : "disabled");
+ v4l2_info(sd, "WSS: %s\n", state->wss_enable ?
+ wss_strs[state->wss_mode] : "disabled");
+ v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled");
+ v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled");
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa7127_core_ops = {
+ .log_status = saa7127_log_status,
+ .g_chip_ident = saa7127_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa7127_g_register,
+ .s_register = saa7127_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops saa7127_video_ops = {
+ .s_vbi_data = saa7127_s_vbi_data,
+ .g_fmt = saa7127_g_fmt,
+ .s_std_output = saa7127_s_std_output,
+ .s_routing = saa7127_s_routing,
+ .s_stream = saa7127_s_stream,
+};
+
+static const struct v4l2_subdev_ops saa7127_ops = {
+ .core = &saa7127_core_ops,
+ .video = &saa7127_video_ops,
+};
+
/* ----------------------------------------------------------------------- */
static int saa7127_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct saa7127_state *state;
+ struct v4l2_subdev *sd;
struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */
/* Check if the adapter supports the needed features */
@@ -674,40 +713,42 @@ static int saa7127_probe(struct i2c_client *client,
v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n",
client->addr << 1);
+ state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa7127_ops);
+
/* First test register 0: Bits 5-7 are a version ID (should be 0),
and bit 2 should also be 0.
This is rather general, so the second test is more specific and
looks at the 'ending point of burst in clock cycles' which is
0x1d after a reset and not expected to ever change. */
- if ((saa7127_read(client, 0) & 0xe4) != 0 ||
- (saa7127_read(client, 0x29) & 0x3f) != 0x1d) {
- v4l_dbg(1, debug, client, "saa7127 not found\n");
+ if ((saa7127_read(sd, 0) & 0xe4) != 0 ||
+ (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) {
+ v4l2_dbg(1, debug, sd, "saa7127 not found\n");
+ kfree(state);
return -ENODEV;
}
- state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL);
-
- if (state == NULL)
- return -ENOMEM;
-
- i2c_set_clientdata(client, state);
/* Configure Encoder */
- v4l_dbg(1, debug, client, "Configuring encoder\n");
- saa7127_write_inittab(client, saa7127_init_config_common);
- saa7127_set_std(client, V4L2_STD_NTSC);
- saa7127_set_output_type(client, SAA7127_OUTPUT_TYPE_BOTH);
- saa7127_set_vps(client, &vbi);
- saa7127_set_wss(client, &vbi);
- saa7127_set_cc(client, &vbi);
- saa7127_set_xds(client, &vbi);
+ v4l2_dbg(1, debug, sd, "Configuring encoder\n");
+ saa7127_write_inittab(sd, saa7127_init_config_common);
+ saa7127_set_std(sd, V4L2_STD_NTSC);
+ saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH);
+ saa7127_set_vps(sd, &vbi);
+ saa7127_set_wss(sd, &vbi);
+ saa7127_set_cc(sd, &vbi);
+ saa7127_set_xds(sd, &vbi);
if (test_image == 1)
/* The Encoder has an internal Colorbar generator */
/* This can be used for debugging */
- saa7127_set_input_type(client, SAA7127_INPUT_TYPE_TEST_IMAGE);
+ saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE);
else
- saa7127_set_input_type(client, SAA7127_INPUT_TYPE_NORMAL);
- saa7127_set_video_enable(client, 1);
+ saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL);
+ saa7127_set_video_enable(sd, 1);
if (id->driver_data) { /* Chip type is already known */
state->ident = id->driver_data;
@@ -715,10 +756,10 @@ static int saa7127_probe(struct i2c_client *client,
int read_result;
/* Detect if it's an saa7129 */
- read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2);
- saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa);
- if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
- saa7127_write(client, SAA7129_REG_FADE_KEY_COL2,
+ read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2);
+ saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa);
+ if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
+ saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2,
read_result);
state->ident = V4L2_IDENT_SAA7129;
strlcpy(client->name, "saa7129", I2C_NAME_SIZE);
@@ -728,10 +769,10 @@ static int saa7127_probe(struct i2c_client *client,
}
}
- v4l_info(client, "%s found @ 0x%x (%s)\n", client->name,
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
client->addr << 1, client->adapter->name);
if (state->ident == V4L2_IDENT_SAA7129)
- saa7127_write_inittab(client, saa7129_init_config_extra);
+ saa7127_write_inittab(sd, saa7129_init_config_extra);
return 0;
}
@@ -739,9 +780,12 @@ static int saa7127_probe(struct i2c_client *client,
static int saa7127_remove(struct i2c_client *client)
{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
/* Turn off TV output */
- saa7127_set_video_enable(client, 0);
- kfree(i2c_get_clientdata(client));
+ saa7127_set_video_enable(sd, 0);
+ kfree(to_state(sd));
return 0;
}
@@ -760,7 +804,6 @@ MODULE_DEVICE_TABLE(i2c, saa7127_id);
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "saa7127",
.driverid = I2C_DRIVERID_SAA7127,
- .command = saa7127_command,
.probe = saa7127_probe,
.remove = saa7127_remove,
.id_table = saa7127_id,
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index ddc5402c5fb0..a2e3f6729c5b 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -4606,6 +4606,43 @@ struct saa7134_board saa7134_boards[] = {
.gpio = 0x0200000,
},
},
+ [SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG] = {
+ .name = "Kworld Plus TV Analog Lite PCI",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .radio_type = TUNER_TEA5767,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x80000700,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ .gpio = 0x100,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x200,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x200,
+ } },
+ .radio = {
+ .name = name_radio,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0x100,
+ },
+ .mute = {
+ .name = name_mute,
+ .vmux = 8,
+ .amux = 2,
+ },
+ },
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -4736,6 +4773,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.driver_data = SAA7134_BOARD_MD7134,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x16be, /* CTX946 analog TV, HW mpeg, DVB-T */
+ .subdevice = 0x5000, /* only analog TV and DVB-T for now */
+ .driver_data = SAA7134_BOARD_MD7134,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7130,
.subvendor = 0x1048,
.subdevice = 0x226b,
@@ -5653,6 +5696,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subdevice = 0x4878, /* REV:1.02G */
.driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1,
}, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x17de,
+ .subdevice = 0x7128,
+ .driver_data = SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG,
+ }, {
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -5880,6 +5929,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_BEHOLD_507_9FM:
case SAA7134_BOARD_GENIUS_TVGO_A11MCE:
case SAA7134_BOARD_REAL_ANGEL_220:
+ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
dev->has_remote = SAA7134_REMOTE_GPIO;
break;
case SAA7134_BOARD_FLYDVBS_LR300:
@@ -6048,7 +6098,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev)
struct v4l2_priv_tun_config xc2028_cfg;
struct xc2028_ctrl ctl;
- memset(&xc2028_cfg, 0, sizeof(ctl));
+ memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
memset(&ctl, 0, sizeof(ctl));
ctl.fname = XC2028_DEFAULT_FIRMWARE;
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index 8c46115d4c79..d9a5652595b5 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -954,20 +954,14 @@ static int dvb_init(struct saa7134_dev *dev)
/* FIXME: add support for multi-frontend */
mutex_init(&dev->frontends.lock);
INIT_LIST_HEAD(&dev->frontends.felist);
- dev->frontends.active_fe_id = 0;
printk(KERN_INFO "%s() allocating 1 frontend\n", __func__);
-
- if (videobuf_dvb_alloc_frontend(&dev->frontends, 1) == NULL) {
+ fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, 1);
+ if (!fe0) {
printk(KERN_ERR "%s() failed to alloc\n", __func__);
return -ENOMEM;
}
- /* Get the first frontend */
- fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
- if (!fe0)
- return -EINVAL;
-
/* init struct videobuf_dvb */
dev->ts.nr_bufs = 32;
dev->ts.nr_packets = 32*4;
@@ -1376,7 +1370,7 @@ static int dvb_init(struct saa7134_dev *dev)
};
if (!fe0->dvb.frontend)
- return -1;
+ goto dettach_frontend;
fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
if (!fe) {
@@ -1388,7 +1382,7 @@ static int dvb_init(struct saa7134_dev *dev)
if (NULL == fe0->dvb.frontend) {
printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name);
- return -1;
+ goto dettach_frontend;
}
/* define general-purpose callback pointer */
fe0->dvb.frontend->callback = saa7134_tuner_callback;
@@ -1411,11 +1405,8 @@ static int dvb_init(struct saa7134_dev *dev)
return ret;
dettach_frontend:
- if (fe0->dvb.frontend)
- dvb_frontend_detach(fe0->dvb.frontend);
- fe0->dvb.frontend = NULL;
-
- return -1;
+ videobuf_dvb_dealloc_frontends(&dev->frontends);
+ return -EINVAL;
}
static int dvb_fini(struct saa7134_dev *dev)
@@ -1454,8 +1445,7 @@ static int dvb_fini(struct saa7134_dev *dev)
}
}
}
- if (fe0->dvb.frontend)
- videobuf_dvb_unregister_bus(&dev->frontends);
+ videobuf_dvb_unregister_bus(&dev->frontends);
return 0;
}
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index c53fd5f9f6b5..d2124f64e4e2 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -97,6 +97,15 @@ static int build_key(struct saa7134_dev *dev)
dprintk("build_key gpio=0x%x mask=0x%x data=%d\n",
gpio, ir->mask_keycode, data);
+ switch (dev->board) {
+ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
+ if (data == ir->mask_keycode)
+ ir_input_nokey(ir->dev, &ir->ir);
+ else
+ ir_input_keydown(ir->dev, &ir->ir, data, data);
+ return 0;
+ }
+
if (ir->polling) {
if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
(ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
@@ -586,6 +595,11 @@ int saa7134_input_init1(struct saa7134_dev *dev)
mask_keyup = 0x4000;
polling = 50; /* ms */
break;
+ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
+ ir_codes = ir_codes_kworld_plus_tv_analog;
+ mask_keycode = 0x7f;
+ polling = 40; /* ms */
+ break;
}
if (NULL == ir_codes) {
printk("%s: Oops: IR config error [card=%d]\n",
diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c
index c5d0b44c179e..76b16407b01e 100644
--- a/drivers/media/video/saa7134/saa7134-tvaudio.c
+++ b/drivers/media/video/saa7134/saa7134-tvaudio.c
@@ -159,7 +159,7 @@ static struct saa7134_tvaudio tvaudio[] = {
.mode = TVAUDIO_FM_MONO,
}
};
-#define TVAUDIO (sizeof(tvaudio)/sizeof(struct saa7134_tvaudio))
+#define TVAUDIO ARRAY_SIZE(tvaudio)
/* ------------------------------------------------------------------ */
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 24096d6e1ef8..f6c1fcc72070 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -275,8 +275,9 @@ struct saa7134_format {
#define SAA7134_BOARD_REAL_ANGEL_220 150
#define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151
#define SAA7134_BOARD_ASUSTeK_TIGER 152
+#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153
-#define SAA7134_MAXBOARDS 8
+#define SAA7134_MAXBOARDS 32
#define SAA7134_INPUT_MAX 8
/* ----------------------------------------------------------- */
diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c
index af60ede5310d..9befca65905e 100644
--- a/drivers/media/video/saa717x.c
+++ b/drivers/media/video/saa717x.c
@@ -37,7 +37,7 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver");
@@ -54,6 +54,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
*/
struct saa717x_state {
+ struct v4l2_subdev sd;
v4l2_std_id std;
int input;
int enable;
@@ -75,6 +76,11 @@ struct saa717x_state {
int audio_input;
};
+static inline struct saa717x_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa717x_state, sd);
+}
+
/* ----------------------------------------------------------------------- */
/* for audio mode */
@@ -88,8 +94,9 @@ struct saa717x_state {
/* ----------------------------------------------------------------------- */
-static int saa717x_write(struct i2c_client *client, u32 reg, u32 value)
+static int saa717x_write(struct v4l2_subdev *sd, u32 reg, u32 value)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
struct i2c_adapter *adap = client->adapter;
int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488;
unsigned char mm1[6];
@@ -109,20 +116,21 @@ static int saa717x_write(struct i2c_client *client, u32 reg, u32 value)
}
msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */
msg.buf = mm1;
- v4l_dbg(2, debug, client, "wrote: reg 0x%03x=%08x\n", reg, value);
+ v4l2_dbg(2, debug, sd, "wrote: reg 0x%03x=%08x\n", reg, value);
return i2c_transfer(adap, &msg, 1) == 1;
}
-static void saa717x_write_regs(struct i2c_client *client, u32 *data)
+static void saa717x_write_regs(struct v4l2_subdev *sd, u32 *data)
{
while (data[0] || data[1]) {
- saa717x_write(client, data[0], data[1]);
+ saa717x_write(sd, data[0], data[1]);
data += 2;
}
}
-static u32 saa717x_read(struct i2c_client *client, u32 reg)
+static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
struct i2c_adapter *adap = client->adapter;
int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528;
unsigned char mm1[2];
@@ -146,7 +154,7 @@ static u32 saa717x_read(struct i2c_client *client, u32 reg)
else
value = mm2[0] & 0xff;
- v4l_dbg(2, debug, client, "read: reg 0x%03x=0x%08x\n", reg, value);
+ v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value);
return value;
}
@@ -680,7 +688,7 @@ static u32 reg_set_audio_template[4][2] =
/* Get detected audio flags (from saa7134 driver) */
-static void get_inf_dev_status(struct i2c_client *client,
+static void get_inf_dev_status(struct v4l2_subdev *sd,
int *dual_flag, int *stereo_flag)
{
u32 reg_data3;
@@ -719,13 +727,13 @@ static void get_inf_dev_status(struct i2c_client *client,
/* (demdec status: 0x528) */
/* read current status */
- reg_data3 = saa717x_read(client, 0x0528);
+ reg_data3 = saa717x_read(sd, 0x0528);
- v4l_dbg(1, debug, client, "tvaudio thread status: 0x%x [%s%s%s]\n",
+ v4l2_dbg(1, debug, sd, "tvaudio thread status: 0x%x [%s%s%s]\n",
reg_data3, stdres[reg_data3 & 0x1f],
(reg_data3 & 0x000020) ? ",stereo" : "",
(reg_data3 & 0x000040) ? ",dual" : "");
- v4l_dbg(1, debug, client, "detailed status: "
+ v4l2_dbg(1, debug, sd, "detailed status: "
"%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n",
(reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "",
(reg_data3 & 0x000100) ? " A2/EIAJ dual " : "",
@@ -746,51 +754,51 @@ static void get_inf_dev_status(struct i2c_client *client,
(reg_data3 & 0x100000) ? " init done " : "");
if (reg_data3 & 0x000220) {
- v4l_dbg(1, debug, client, "ST!!!\n");
+ v4l2_dbg(1, debug, sd, "ST!!!\n");
*stereo_flag = 1;
}
if (reg_data3 & 0x000140) {
- v4l_dbg(1, debug, client, "DUAL!!!\n");
+ v4l2_dbg(1, debug, sd, "DUAL!!!\n");
*dual_flag = 1;
}
}
/* regs write to set audio mode */
-static void set_audio_mode(struct i2c_client *client, int audio_mode)
+static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode)
{
- v4l_dbg(1, debug, client, "writing registers to set audio mode by set %d\n",
+ v4l2_dbg(1, debug, sd, "writing registers to set audio mode by set %d\n",
audio_mode);
- saa717x_write(client, 0x46c, reg_set_audio_template[audio_mode][0]);
- saa717x_write(client, 0x470, reg_set_audio_template[audio_mode][1]);
+ saa717x_write(sd, 0x46c, reg_set_audio_template[audio_mode][0]);
+ saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]);
}
/* write regs to video output level (bright,contrast,hue,sat) */
-static void set_video_output_level_regs(struct i2c_client *client,
+static void set_video_output_level_regs(struct v4l2_subdev *sd,
struct saa717x_state *decoder)
{
/* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */
- saa717x_write(client, 0x10a, decoder->bright);
+ saa717x_write(sd, 0x10a, decoder->bright);
/* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) -
0h (luminance off) 40: i2c dump
c0h (-1.0 inverse chrominance)
80h (-2.0 inverse chrominance) */
- saa717x_write(client, 0x10b, decoder->contrast);
+ saa717x_write(sd, 0x10b, decoder->contrast);
/* saturation? 7fh(max)-40h(ITU)-0h(color off)
c0h (-1.0 inverse chrominance)
80h (-2.0 inverse chrominance) */
- saa717x_write(client, 0x10c, decoder->sat);
+ saa717x_write(sd, 0x10c, decoder->sat);
/* color hue (phase) control
7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */
- saa717x_write(client, 0x10d, decoder->hue);
+ saa717x_write(sd, 0x10d, decoder->hue);
}
/* write regs to set audio volume, bass and treble */
-static int set_audio_regs(struct i2c_client *client,
+static int set_audio_regs(struct v4l2_subdev *sd,
struct saa717x_state *decoder)
{
u8 mute = 0xac; /* -84 dB */
@@ -798,8 +806,8 @@ static int set_audio_regs(struct i2c_client *client,
unsigned int work_l, work_r;
/* set SIF analog I/O select */
- saa717x_write(client, 0x0594, decoder->audio_input);
- v4l_dbg(1, debug, client, "set audio input %d\n",
+ saa717x_write(sd, 0x0594, decoder->audio_input);
+ v4l2_dbg(1, debug, sd, "set audio input %d\n",
decoder->audio_input);
/* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */
@@ -819,17 +827,17 @@ static int set_audio_regs(struct i2c_client *client,
((u8)decoder->audio_main_vol_r << 8);
}
- saa717x_write(client, 0x480, val);
+ saa717x_write(sd, 0x480, val);
/* bass and treble; go to another function */
/* set bass and treble */
val = decoder->audio_main_bass | (decoder->audio_main_treble << 8);
- saa717x_write(client, 0x488, val);
+ saa717x_write(sd, 0x488, val);
return 0;
}
/********** scaling staff ***********/
-static void set_h_prescale(struct i2c_client *client,
+static void set_h_prescale(struct v4l2_subdev *sd,
int task, int prescale)
{
static const struct {
@@ -862,107 +870,101 @@ static void set_h_prescale(struct i2c_client *client,
return;
/* horizonal prescaling */
- saa717x_write(client, 0x60 + task_shift, vals[i].xpsc);
+ saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc);
/* accumulation length */
- saa717x_write(client, 0x61 + task_shift, vals[i].xacl);
+ saa717x_write(sd, 0x61 + task_shift, vals[i].xacl);
/* level control */
- saa717x_write(client, 0x62 + task_shift,
+ saa717x_write(sd, 0x62 + task_shift,
(vals[i].xc2_1 << 3) | vals[i].xdcg);
/*FIR prefilter control */
- saa717x_write(client, 0x63 + task_shift,
+ saa717x_write(sd, 0x63 + task_shift,
(vals[i].vpfy << 2) | vals[i].vpfy);
}
/********** scaling staff ***********/
-static void set_v_scale(struct i2c_client *client, int task, int yscale)
+static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale)
{
int task_shift;
task_shift = task * 0x40;
/* Vertical scaling ratio (LOW) */
- saa717x_write(client, 0x70 + task_shift, yscale & 0xff);
+ saa717x_write(sd, 0x70 + task_shift, yscale & 0xff);
/* Vertical scaling ratio (HI) */
- saa717x_write(client, 0x71 + task_shift, yscale >> 8);
-}
-
-static int saa717x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
-{
- /* not yet implament, so saa717x_cfg_??hz_??_audio is not defined. */
- return 0;
+ saa717x_write(sd, 0x71 + task_shift, yscale >> 8);
}
-static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct saa717x_state *state = i2c_get_clientdata(client);
+ struct saa717x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value < 0 || ctrl->value > 255) {
- v4l_err(client, "invalid brightness setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
return -ERANGE;
}
state->bright = ctrl->value;
- v4l_dbg(1, debug, client, "bright:%d\n", state->bright);
- saa717x_write(client, 0x10a, state->bright);
+ v4l2_dbg(1, debug, sd, "bright:%d\n", state->bright);
+ saa717x_write(sd, 0x10a, state->bright);
break;
case V4L2_CID_CONTRAST:
if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid contrast setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
return -ERANGE;
}
state->contrast = ctrl->value;
- v4l_dbg(1, debug, client, "contrast:%d\n", state->contrast);
- saa717x_write(client, 0x10b, state->contrast);
+ v4l2_dbg(1, debug, sd, "contrast:%d\n", state->contrast);
+ saa717x_write(sd, 0x10b, state->contrast);
break;
case V4L2_CID_SATURATION:
if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid saturation setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
return -ERANGE;
}
state->sat = ctrl->value;
- v4l_dbg(1, debug, client, "sat:%d\n", state->sat);
- saa717x_write(client, 0x10c, state->sat);
+ v4l2_dbg(1, debug, sd, "sat:%d\n", state->sat);
+ saa717x_write(sd, 0x10c, state->sat);
break;
case V4L2_CID_HUE:
if (ctrl->value < -127 || ctrl->value > 127) {
- v4l_err(client, "invalid hue setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
return -ERANGE;
}
state->hue = ctrl->value;
- v4l_dbg(1, debug, client, "hue:%d\n", state->hue);
- saa717x_write(client, 0x10d, state->hue);
+ v4l2_dbg(1, debug, sd, "hue:%d\n", state->hue);
+ saa717x_write(sd, 0x10d, state->hue);
break;
case V4L2_CID_AUDIO_MUTE:
state->audio_main_mute = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
case V4L2_CID_AUDIO_VOLUME:
state->audio_main_volume = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
case V4L2_CID_AUDIO_BALANCE:
state->audio_main_balance = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
case V4L2_CID_AUDIO_TREBLE:
state->audio_main_treble = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
case V4L2_CID_AUDIO_BASS:
state->audio_main_bass = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
default:
@@ -972,9 +974,9 @@ static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c
return 0;
}
-static int saa717x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa717x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct saa717x_state *state = i2c_get_clientdata(client);
+ struct saa717x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
@@ -1103,13 +1105,15 @@ static struct v4l2_queryctrl saa717x_qctrl[] = {
},
};
-static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_state *decoder, int inp)
+static int saa717x_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
+ struct saa717x_state *decoder = to_state(sd);
+ int inp = route->input;
int is_tuner = inp & 0x80; /* tuner input flag */
inp &= 0x7f;
- v4l_dbg(1, debug, client, "decoder set input (%d)\n", inp);
+ v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", inp);
/* inputs from 0-9 are available*/
/* saa717x have mode0-mode9 but mode5 is reserved. */
if (inp < 0 || inp > 9 || inp == 5)
@@ -1119,222 +1123,197 @@ static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_sta
int input_line = inp;
decoder->input = input_line;
- v4l_dbg(1, debug, client, "now setting %s input %d\n",
+ v4l2_dbg(1, debug, sd, "now setting %s input %d\n",
input_line >= 6 ? "S-Video" : "Composite",
input_line);
/* select mode */
- saa717x_write(client, 0x102,
- (saa717x_read(client, 0x102) & 0xf0) |
+ saa717x_write(sd, 0x102,
+ (saa717x_read(sd, 0x102) & 0xf0) |
input_line);
/* bypass chrominance trap for modes 6..9 */
- saa717x_write(client, 0x109,
- (saa717x_read(client, 0x109) & 0x7f) |
+ saa717x_write(sd, 0x109,
+ (saa717x_read(sd, 0x109) & 0x7f) |
(input_line < 6 ? 0x0 : 0x80));
/* change audio_mode */
if (is_tuner) {
/* tuner */
- set_audio_mode(client, decoder->tuner_audio_mode);
+ set_audio_mode(sd, decoder->tuner_audio_mode);
} else {
/* Force to STEREO mode if Composite or
* S-Video were chosen */
- set_audio_mode(client, TUNER_AUDIO_STEREO);
+ set_audio_mode(sd, TUNER_AUDIO_STEREO);
}
/* change initialize procedure (Composite/S-Video) */
if (is_tuner)
- saa717x_write_regs(client, reg_init_tuner_input);
+ saa717x_write_regs(sd, reg_init_tuner_input);
else if (input_line >= 6)
- saa717x_write_regs(client, reg_init_svideo_input);
+ saa717x_write_regs(sd, reg_init_svideo_input);
else
- saa717x_write_regs(client, reg_init_composite_input);
+ saa717x_write_regs(sd, reg_init_composite_input);
}
return 0;
}
-static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int saa717x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
- struct saa717x_state *decoder = i2c_get_clientdata(client);
-
- v4l_dbg(1, debug, client, "IOCTL: %08x\n", cmd);
+ int i;
- switch (cmd) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- return saa717x_set_audio_clock_freq(client, *(u32 *)arg);
+ for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++)
+ if (qc->id && qc->id == saa717x_qctrl[i].id) {
+ memcpy(qc, &saa717x_qctrl[i], sizeof(*qc));
+ return 0;
+ }
+ return -EINVAL;
+}
- case VIDIOC_G_CTRL:
- return saa717x_get_v4lctrl(client, (struct v4l2_control *)arg);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_S_CTRL:
- return saa717x_set_v4lctrl(client, (struct v4l2_control *)arg);
+ if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa717x_read(sd, reg->reg);
+ return 0;
+}
- case VIDIOC_QUERYCTRL: {
- struct v4l2_queryctrl *qc = arg;
- int i;
+static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 addr = reg->reg & 0xffff;
+ u8 val = reg->val & 0xff;
- for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++)
- if (qc->id && qc->id == saa717x_qctrl[i].id) {
- memcpy(qc, &saa717x_qctrl[i], sizeof(*qc));
- return 0;
- }
+ if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
return -EINVAL;
- }
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER: {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- reg->val = saa717x_read(client, reg->reg);
- break;
- }
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa717x_write(sd, addr, val);
+ return 0;
+}
+#endif
- case VIDIOC_DBG_S_REGISTER: {
- struct v4l2_register *reg = arg;
- u16 addr = reg->reg & 0xffff;
- u8 val = reg->val & 0xff;
+static int saa717x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct v4l2_pix_format *pix;
+ int prescale, h_scale, v_scale;
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- saa717x_write(client, addr, val);
- break;
- }
-#endif
+ pix = &fmt->fmt.pix;
+ v4l2_dbg(1, debug, sd, "decoder set size\n");
- case VIDIOC_S_FMT: {
- struct v4l2_format *fmt = (struct v4l2_format *)arg;
- struct v4l2_pix_format *pix;
- int prescale, h_scale, v_scale;
-
- pix = &fmt->fmt.pix;
- v4l_dbg(1, debug, client, "decoder set size\n");
-
- /* FIXME need better bounds checking here */
- if (pix->width < 1 || pix->width > 1440)
- return -EINVAL;
- if (pix->height < 1 || pix->height > 960)
- return -EINVAL;
-
- /* scaling setting */
- /* NTSC and interlace only */
- prescale = SAA717X_NTSC_WIDTH / pix->width;
- if (prescale == 0)
- prescale = 1;
- h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width;
- /* interlace */
- v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height;
-
- /* Horizontal prescaling etc */
- set_h_prescale(client, 0, prescale);
- set_h_prescale(client, 1, prescale);
-
- /* Horizontal scaling increment */
- /* TASK A */
- saa717x_write(client, 0x6C, (u8)(h_scale & 0xFF));
- saa717x_write(client, 0x6D, (u8)((h_scale >> 8) & 0xFF));
- /* TASK B */
- saa717x_write(client, 0xAC, (u8)(h_scale & 0xFF));
- saa717x_write(client, 0xAD, (u8)((h_scale >> 8) & 0xFF));
-
- /* Vertical prescaling etc */
- set_v_scale(client, 0, v_scale);
- set_v_scale(client, 1, v_scale);
-
- /* set video output size */
- /* video number of pixels at output */
- /* TASK A */
- saa717x_write(client, 0x5C, (u8)(pix->width & 0xFF));
- saa717x_write(client, 0x5D, (u8)((pix->width >> 8) & 0xFF));
- /* TASK B */
- saa717x_write(client, 0x9C, (u8)(pix->width & 0xFF));
- saa717x_write(client, 0x9D, (u8)((pix->width >> 8) & 0xFF));
-
- /* video number of lines at output */
- /* TASK A */
- saa717x_write(client, 0x5E, (u8)(pix->height & 0xFF));
- saa717x_write(client, 0x5F, (u8)((pix->height >> 8) & 0xFF));
- /* TASK B */
- saa717x_write(client, 0x9E, (u8)(pix->height & 0xFF));
- saa717x_write(client, 0x9F, (u8)((pix->height >> 8) & 0xFF));
- break;
- }
+ /* FIXME need better bounds checking here */
+ if (pix->width < 1 || pix->width > 1440)
+ return -EINVAL;
+ if (pix->height < 1 || pix->height > 960)
+ return -EINVAL;
- case AUDC_SET_RADIO:
- decoder->radio = 1;
- break;
+ /* scaling setting */
+ /* NTSC and interlace only */
+ prescale = SAA717X_NTSC_WIDTH / pix->width;
+ if (prescale == 0)
+ prescale = 1;
+ h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width;
+ /* interlace */
+ v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height;
+
+ /* Horizontal prescaling etc */
+ set_h_prescale(sd, 0, prescale);
+ set_h_prescale(sd, 1, prescale);
+
+ /* Horizontal scaling increment */
+ /* TASK A */
+ saa717x_write(sd, 0x6C, (u8)(h_scale & 0xFF));
+ saa717x_write(sd, 0x6D, (u8)((h_scale >> 8) & 0xFF));
+ /* TASK B */
+ saa717x_write(sd, 0xAC, (u8)(h_scale & 0xFF));
+ saa717x_write(sd, 0xAD, (u8)((h_scale >> 8) & 0xFF));
+
+ /* Vertical prescaling etc */
+ set_v_scale(sd, 0, v_scale);
+ set_v_scale(sd, 1, v_scale);
+
+ /* set video output size */
+ /* video number of pixels at output */
+ /* TASK A */
+ saa717x_write(sd, 0x5C, (u8)(pix->width & 0xFF));
+ saa717x_write(sd, 0x5D, (u8)((pix->width >> 8) & 0xFF));
+ /* TASK B */
+ saa717x_write(sd, 0x9C, (u8)(pix->width & 0xFF));
+ saa717x_write(sd, 0x9D, (u8)((pix->width >> 8) & 0xFF));
+
+ /* video number of lines at output */
+ /* TASK A */
+ saa717x_write(sd, 0x5E, (u8)(pix->height & 0xFF));
+ saa717x_write(sd, 0x5F, (u8)((pix->height >> 8) & 0xFF));
+ /* TASK B */
+ saa717x_write(sd, 0x9E, (u8)(pix->height & 0xFF));
+ saa717x_write(sd, 0x9F, (u8)((pix->height >> 8) & 0xFF));
+ return 0;
+}
- case VIDIOC_S_STD: {
- v4l2_std_id std = *(v4l2_std_id *) arg;
+static int saa717x_s_radio(struct v4l2_subdev *sd)
+{
+ struct saa717x_state *decoder = to_state(sd);
- v4l_dbg(1, debug, client, "decoder set norm ");
- v4l_dbg(1, debug, client, "(not yet implementd)\n");
+ decoder->radio = 1;
+ return 0;
+}
- decoder->radio = 0;
- decoder->std = std;
- break;
- }
+static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa717x_state *decoder = to_state(sd);
- case VIDIOC_INT_G_AUDIO_ROUTING: {
- struct v4l2_routing *route = arg;
+ v4l2_dbg(1, debug, sd, "decoder set norm ");
+ v4l2_dbg(1, debug, sd, "(not yet implementd)\n");
- route->input = decoder->audio_input;
- route->output = 0;
- break;
- }
+ decoder->radio = 0;
+ decoder->std = std;
+ return 0;
+}
- case VIDIOC_INT_S_AUDIO_ROUTING: {
- struct v4l2_routing *route = arg;
+static int saa717x_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct saa717x_state *decoder = to_state(sd);
- if (route->input < 3) { /* FIXME! --tadachi */
- decoder->audio_input = route->input;
- v4l_dbg(1, debug, client,
+ if (route->input < 3) { /* FIXME! --tadachi */
+ decoder->audio_input = route->input;
+ v4l2_dbg(1, debug, sd,
"set decoder audio input to %d\n",
decoder->audio_input);
- set_audio_regs(client, decoder);
- break;
- }
- return -ERANGE;
- }
-
- case VIDIOC_INT_S_VIDEO_ROUTING: {
- struct v4l2_routing *route = arg;
- int inp = route->input;
-
- return saa717x_set_video_input(client, decoder, inp);
+ set_audio_regs(sd, decoder);
+ return 0;
}
+ return -ERANGE;
+}
- case VIDIOC_STREAMON: {
- v4l_dbg(1, debug, client, "decoder enable output\n");
- decoder->enable = 1;
- saa717x_write(client, 0x193, 0xa6);
- break;
- }
+static int saa717x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa717x_state *decoder = to_state(sd);
- case VIDIOC_STREAMOFF: {
- v4l_dbg(1, debug, client, "decoder disable output\n");
- decoder->enable = 0;
- saa717x_write(client, 0x193, 0x26); /* right? FIXME!--tadachi */
- break;
- }
+ v4l2_dbg(1, debug, sd, "decoder %s output\n",
+ enable ? "enable" : "disable");
+ decoder->enable = enable;
+ saa717x_write(sd, 0x193, enable ? 0xa6 : 0x26);
+ return 0;
+}
- /* change audio mode */
- case VIDIOC_S_TUNER: {
- struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
- int audio_mode;
- char *mes[4] = {
- "MONO", "STEREO", "LANG1", "LANG2/SAP"
- };
+/* change audio mode */
+static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct saa717x_state *decoder = to_state(sd);
+ int audio_mode;
+ char *mes[4] = {
+ "MONO", "STEREO", "LANG1", "LANG2/SAP"
+ };
- audio_mode = V4L2_TUNER_MODE_STEREO;
+ audio_mode = V4L2_TUNER_MODE_STEREO;
- switch (vt->audmode) {
+ switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO:
audio_mode = TUNER_AUDIO_MONO;
break;
@@ -1347,70 +1326,101 @@ static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg)
case V4L2_TUNER_MODE_LANG1:
audio_mode = TUNER_AUDIO_LANG1;
break;
- }
-
- v4l_dbg(1, debug, client, "change audio mode to %s\n",
- mes[audio_mode]);
- decoder->tuner_audio_mode = audio_mode;
- /* The registers are not changed here. */
- /* See DECODER_ENABLE_OUTPUT section. */
- set_audio_mode(client, decoder->tuner_audio_mode);
- break;
}
- case VIDIOC_G_TUNER: {
- struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
- int dual_f, stereo_f;
+ v4l2_dbg(1, debug, sd, "change audio mode to %s\n",
+ mes[audio_mode]);
+ decoder->tuner_audio_mode = audio_mode;
+ /* The registers are not changed here. */
+ /* See DECODER_ENABLE_OUTPUT section. */
+ set_audio_mode(sd, decoder->tuner_audio_mode);
+ return 0;
+}
- if (decoder->radio)
- break;
- get_inf_dev_status(client, &dual_f, &stereo_f);
+static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct saa717x_state *decoder = to_state(sd);
+ int dual_f, stereo_f;
- v4l_dbg(1, debug, client, "DETECT==st:%d dual:%d\n",
- stereo_f, dual_f);
+ if (decoder->radio)
+ return 0;
+ get_inf_dev_status(sd, &dual_f, &stereo_f);
- /* mono */
- if ((dual_f == 0) && (stereo_f == 0)) {
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- v4l_dbg(1, debug, client, "DETECT==MONO\n");
- }
+ v4l2_dbg(1, debug, sd, "DETECT==st:%d dual:%d\n",
+ stereo_f, dual_f);
- /* stereo */
- if (stereo_f == 1) {
- if (vt->audmode == V4L2_TUNER_MODE_STEREO ||
- vt->audmode == V4L2_TUNER_MODE_LANG1) {
- vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
- v4l_dbg(1, debug, client, "DETECT==ST(ST)\n");
- } else {
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- v4l_dbg(1, debug, client, "DETECT==ST(MONO)\n");
- }
- }
+ /* mono */
+ if ((dual_f == 0) && (stereo_f == 0)) {
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==MONO\n");
+ }
- /* dual */
- if (dual_f == 1) {
- if (vt->audmode == V4L2_TUNER_MODE_LANG2) {
- vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO;
- v4l_dbg(1, debug, client, "DETECT==DUAL1\n");
- } else {
- vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO;
- v4l_dbg(1, debug, client, "DETECT==DUAL2\n");
- }
+ /* stereo */
+ if (stereo_f == 1) {
+ if (vt->audmode == V4L2_TUNER_MODE_STEREO ||
+ vt->audmode == V4L2_TUNER_MODE_LANG1) {
+ vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ v4l2_dbg(1, debug, sd, "DETECT==ST(ST)\n");
+ } else {
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==ST(MONO)\n");
}
- break;
}
- case VIDIOC_LOG_STATUS:
- /* not yet implemented */
- break;
-
- default:
- return -EINVAL;
+ /* dual */
+ if (dual_f == 1) {
+ if (vt->audmode == V4L2_TUNER_MODE_LANG2) {
+ vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==DUAL1\n");
+ } else {
+ vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==DUAL2\n");
+ }
}
-
return 0;
}
+static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa717x_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa717x_g_register,
+ .s_register = saa717x_s_register,
+#endif
+ .queryctrl = saa717x_queryctrl,
+ .g_ctrl = saa717x_g_ctrl,
+ .s_ctrl = saa717x_s_ctrl,
+};
+
+static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {
+ .g_tuner = saa717x_g_tuner,
+ .s_tuner = saa717x_s_tuner,
+ .s_std = saa717x_s_std,
+ .s_radio = saa717x_s_radio,
+};
+
+static const struct v4l2_subdev_video_ops saa717x_video_ops = {
+ .s_routing = saa717x_s_video_routing,
+ .s_fmt = saa717x_s_fmt,
+ .s_stream = saa717x_s_stream,
+};
+
+static const struct v4l2_subdev_audio_ops saa717x_audio_ops = {
+ .s_routing = saa717x_s_audio_routing,
+};
+
+static const struct v4l2_subdev_ops saa717x_ops = {
+ .core = &saa717x_core_ops,
+ .tuner = &saa717x_tuner_ops,
+ .audio = &saa717x_audio_ops,
+ .video = &saa717x_video_ops,
+};
+
/* ----------------------------------------------------------------------- */
@@ -1421,6 +1431,7 @@ static int saa717x_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct saa717x_state *decoder;
+ struct v4l2_subdev *sd;
u8 id = 0;
char *p = "";
@@ -1428,13 +1439,21 @@ static int saa717x_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- if (saa717x_write(client, 0x5a4, 0xfe) &&
- saa717x_write(client, 0x5a5, 0x0f) &&
- saa717x_write(client, 0x5a6, 0x00) &&
- saa717x_write(client, 0x5a7, 0x01))
- id = saa717x_read(client, 0x5a0);
+ decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL);
+ if (decoder == NULL)
+ return -ENOMEM;
+
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa717x_ops);
+
+ if (saa717x_write(sd, 0x5a4, 0xfe) &&
+ saa717x_write(sd, 0x5a5, 0x0f) &&
+ saa717x_write(sd, 0x5a6, 0x00) &&
+ saa717x_write(sd, 0x5a7, 0x01))
+ id = saa717x_read(sd, 0x5a0);
if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) {
- v4l_dbg(1, debug, client, "saa717x not found (id=%02x)\n", id);
+ v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id);
+ kfree(decoder);
return -ENODEV;
}
if (id == 0xc2)
@@ -1445,14 +1464,8 @@ static int saa717x_probe(struct i2c_client *client,
p = "saa7174HL";
else
p = "saa7171";
- v4l_info(client, "%s found @ 0x%x (%s)\n", p,
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", p,
client->addr << 1, client->adapter->name);
-
- decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL);
- i2c_set_clientdata(client, decoder);
-
- if (decoder == NULL)
- return -ENOMEM;
decoder->std = V4L2_STD_NTSC;
decoder->input = -1;
decoder->enable = 1;
@@ -1481,15 +1494,15 @@ static int saa717x_probe(struct i2c_client *client,
decoder->audio_main_volume =
(decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40));
- v4l_dbg(1, debug, client, "writing init values\n");
+ v4l2_dbg(1, debug, sd, "writing init values\n");
/* FIXME!! */
- saa717x_write_regs(client, reg_init_initialize);
- set_video_output_level_regs(client, decoder);
+ saa717x_write_regs(sd, reg_init_initialize);
+ set_video_output_level_regs(sd, decoder);
/* set bass,treble to 0db 20041101 K.Ohta */
decoder->audio_main_bass = 0;
decoder->audio_main_treble = 0;
- set_audio_regs(client, decoder);
+ set_audio_regs(sd, decoder);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2*HZ);
@@ -1498,7 +1511,10 @@ static int saa717x_probe(struct i2c_client *client,
static int saa717x_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c
index 044a2e94c34d..d652f25eef0e 100644
--- a/drivers/media/video/se401.c
+++ b/drivers/media/video/se401.c
@@ -975,8 +975,7 @@ static int se401_close(struct inode *inode, struct file *file)
return 0;
}
-static int se401_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int se401_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = file->private_data;
struct usb_se401 *se401 = (struct usb_se401 *)vdev;
@@ -1142,7 +1141,7 @@ static int se401_do_ioctl(struct inode *inode, struct file *file,
static int se401_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, se401_do_ioctl);
+ return video_usercopy(file, cmd, arg, se401_do_ioctl);
}
static ssize_t se401_read(struct file *file, char __user *buf,
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 536b1a9b310c..9a2586b07a05 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -29,7 +29,6 @@
#include <linux/version.h>
#include <linux/device.h>
#include <linux/platform_device.h>
-#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/clk.h>
@@ -75,8 +74,6 @@
#define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */
#define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */
-static DEFINE_MUTEX(camera_lock);
-
/* per video frame buffer */
struct sh_mobile_ceu_buffer {
struct videobuf_buffer vb; /* v4l buffer must be first */
@@ -97,18 +94,20 @@ struct sh_mobile_ceu_dev {
spinlock_t lock;
struct list_head capture;
struct videobuf_buffer *active;
+ int is_interlace;
struct sh_mobile_ceu_info *pdata;
+
+ const struct soc_camera_data_format *camera_fmt;
};
static void ceu_write(struct sh_mobile_ceu_dev *priv,
- unsigned long reg_offs, unsigned long data)
+ unsigned long reg_offs, u32 data)
{
iowrite32(data, priv->base + reg_offs);
}
-static unsigned long ceu_read(struct sh_mobile_ceu_dev *priv,
- unsigned long reg_offs)
+static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
{
return ioread32(priv->base + reg_offs);
}
@@ -156,21 +155,52 @@ static void free_buffer(struct videobuf_queue *vq,
buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
+#define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */
+#define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */
+#define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */
+#define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */
+
+
static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
{
- ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~1);
- ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & 0x0317f313);
- ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | 1);
-
- ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~0x10000);
+ struct soc_camera_device *icd = pcdev->icd;
+ dma_addr_t phys_addr_top, phys_addr_bottom;
- ceu_write(pcdev, CETCR, 0x0317f313 ^ 0x10);
+ /* The hardware is _very_ picky about this sequence. Especially
+ * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge
+ * several not-so-well documented interrupt sources in CETCR.
+ */
+ ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_CPEIE);
+ ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & CEU_CETCR_MAGIC);
+ ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_CPEIE);
+ ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP);
+ ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW);
+
+ if (!pcdev->active)
+ return;
+
+ phys_addr_top = videobuf_to_dma_contig(pcdev->active);
+ ceu_write(pcdev, CDAYR, phys_addr_top);
+ if (pcdev->is_interlace) {
+ phys_addr_bottom = phys_addr_top + icd->width;
+ ceu_write(pcdev, CDBYR, phys_addr_bottom);
+ }
- if (pcdev->active) {
- pcdev->active->state = VIDEOBUF_ACTIVE;
- ceu_write(pcdev, CDAYR, videobuf_to_dma_contig(pcdev->active));
- ceu_write(pcdev, CAPSR, 0x1); /* start capture */
+ switch (icd->current_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ phys_addr_top += icd->width * icd->height;
+ ceu_write(pcdev, CDACR, phys_addr_top);
+ if (pcdev->is_interlace) {
+ phys_addr_bottom = phys_addr_top + icd->width;
+ ceu_write(pcdev, CDBCR, phys_addr_bottom);
+ }
}
+
+ pcdev->active->state = VIDEOBUF_ACTIVE;
+ ceu_write(pcdev, CAPSR, 0x1); /* start capture */
}
static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq,
@@ -292,14 +322,13 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
return IRQ_HANDLED;
}
+/* Called with .video_lock held */
static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
int ret = -EBUSY;
- mutex_lock(&camera_lock);
-
if (pcdev->icd)
goto err;
@@ -319,11 +348,10 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
pcdev->icd = icd;
err:
- mutex_unlock(&camera_lock);
-
return ret;
}
+/* Called with .video_lock held */
static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
@@ -362,8 +390,9 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- int ret, buswidth, width, cfszr_width, cdwdr_width;
+ int ret, buswidth, width, height, cfszr_width, cdwdr_width;
unsigned long camera_flags, common_flags, value;
+ int yuv_mode, yuv_lineskip;
camera_flags = icd->ops->query_bus_param(icd);
common_flags = soc_camera_bus_param_compatible(camera_flags,
@@ -389,27 +418,71 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
ceu_write(pcdev, CRCNTR, 0);
ceu_write(pcdev, CRCMPR, 0);
- value = 0x00000010;
- value |= (common_flags & SOCAM_VSYNC_ACTIVE_LOW) ? (1 << 1) : 0;
- value |= (common_flags & SOCAM_HSYNC_ACTIVE_LOW) ? (1 << 0) : 0;
- value |= (buswidth == 16) ? (1 << 12) : 0;
+ value = 0x00000010; /* data fetch by default */
+ yuv_mode = yuv_lineskip = 0;
+
+ switch (icd->current_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */
+ /* fall-through */
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ yuv_mode = 1;
+ switch (pcdev->camera_fmt->fourcc) {
+ case V4L2_PIX_FMT_UYVY:
+ value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */
+ break;
+ case V4L2_PIX_FMT_YVYU:
+ value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ if (icd->current_fmt->fourcc == V4L2_PIX_FMT_NV21 ||
+ icd->current_fmt->fourcc == V4L2_PIX_FMT_NV61)
+ value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
+
+ value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
+ value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
+ value |= buswidth == 16 ? 1 << 12 : 0;
ceu_write(pcdev, CAMCR, value);
ceu_write(pcdev, CAPCR, 0x00300000);
- ceu_write(pcdev, CAIFR, 0);
+ ceu_write(pcdev, CAIFR, (pcdev->is_interlace) ? 0x101 : 0);
mdelay(1);
- width = icd->width * (icd->current_fmt->depth / 8);
- width = (buswidth == 16) ? width / 2 : width;
- cfszr_width = (buswidth == 8) ? width / 2 : width;
- cdwdr_width = (buswidth == 16) ? width * 2 : width;
+ if (yuv_mode) {
+ width = icd->width * 2;
+ width = buswidth == 16 ? width / 2 : width;
+ cfszr_width = cdwdr_width = icd->width;
+ } else {
+ width = icd->width * ((icd->current_fmt->depth + 7) >> 3);
+ width = buswidth == 16 ? width / 2 : width;
+ cfszr_width = buswidth == 8 ? width / 2 : width;
+ cdwdr_width = buswidth == 16 ? width * 2 : width;
+ }
+
+ height = icd->height;
+ if (pcdev->is_interlace) {
+ height /= 2;
+ cdwdr_width *= 2;
+ }
ceu_write(pcdev, CAMOR, 0);
- ceu_write(pcdev, CAPWR, (icd->height << 16) | width);
- ceu_write(pcdev, CFLCR, 0); /* data fetch mode - no scaling */
- ceu_write(pcdev, CFSZR, (icd->height << 16) | cfszr_width);
- ceu_write(pcdev, CLFCR, 0); /* data fetch mode - no lowpass filter */
+ ceu_write(pcdev, CAPWR, (height << 16) | width);
+ ceu_write(pcdev, CFLCR, 0); /* no scaling */
+ ceu_write(pcdev, CFSZR, (height << 16) | cfszr_width);
+ ceu_write(pcdev, CLFCR, 0); /* no lowpass filter */
/* A few words about byte order (observed in Big Endian mode)
*
@@ -423,19 +496,20 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
* using 7 we swap the data bytes to match the incoming order:
* D0, D1, D2, D3, D4, D5, D6, D7
*/
- ceu_write(pcdev, CDOCR, 0x00000017);
+ value = 0x00000017;
+ if (yuv_lineskip)
+ value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */
+
+ ceu_write(pcdev, CDOCR, value);
ceu_write(pcdev, CDWDR, cdwdr_width);
ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
/* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */
- /* in data fetch mode: no need for CDACR, CDBYR, CDBCR */
-
return 0;
}
-static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
- __u32 pixfmt)
+static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
@@ -450,15 +524,123 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
return 0;
}
-static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect)
+static const struct soc_camera_data_format sh_mobile_ceu_formats[] = {
+ {
+ .name = "NV12",
+ .depth = 12,
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .name = "NV21",
+ .depth = 12,
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .name = "NV16",
+ .depth = 16,
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .name = "NV61",
+ .depth = 16,
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+};
+
+static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ int ret, k, n;
+ int formats = 0;
+
+ ret = sh_mobile_ceu_try_bus_param(icd);
+ if (ret < 0)
+ return 0;
+
+ switch (icd->formats[idx].fourcc) {
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ n = ARRAY_SIZE(sh_mobile_ceu_formats);
+ formats += n;
+ for (k = 0; xlate && k < n; k++) {
+ xlate->host_fmt = &sh_mobile_ceu_formats[k];
+ xlate->cam_fmt = icd->formats + idx;
+ xlate->buswidth = icd->formats[idx].depth;
+ xlate++;
+ dev_dbg(&ici->dev, "Providing format %s using %s\n",
+ sh_mobile_ceu_formats[k].name,
+ icd->formats[idx].name);
+ }
+ default:
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = icd->formats + idx;
+ xlate->cam_fmt = icd->formats + idx;
+ xlate->buswidth = icd->formats[idx].depth;
+ xlate++;
+ dev_dbg(&ici->dev,
+ "Providing format %s in pass-through mode\n",
+ icd->formats[idx].name);
+ }
+ }
+
+ return formats;
+}
+
+static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
{
- return icd->ops->set_fmt_cap(icd, pixfmt, rect);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ const struct soc_camera_format_xlate *xlate;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ switch (pixfmt) {
+ case 0: /* Only geometry change */
+ ret = icd->ops->set_fmt(icd, pixfmt, rect);
+ break;
+ default:
+ ret = icd->ops->set_fmt(icd, xlate->cam_fmt->fourcc, rect);
+ }
+
+ if (pixfmt && !ret) {
+ icd->buswidth = xlate->buswidth;
+ icd->current_fmt = xlate->host_fmt;
+ pcdev->camera_fmt = xlate->cam_fmt;
+ }
+
+ return ret;
}
-static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ const struct soc_camera_format_xlate *xlate;
+ __u32 pixfmt = f->fmt.pix.pixelformat;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
/* FIXME: calculate using depth and bus width */
if (f->fmt.pix.height < 4)
@@ -472,8 +654,31 @@ static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd,
f->fmt.pix.width &= ~0x01;
f->fmt.pix.height &= ~0x03;
+ f->fmt.pix.bytesperline = f->fmt.pix.width *
+ DIV_ROUND_UP(xlate->host_fmt->depth, 8);
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
/* limit to sensor capabilities */
- return icd->ops->try_fmt_cap(icd, f);
+ ret = icd->ops->try_fmt(icd, f);
+ if (ret < 0)
+ return ret;
+
+ switch (f->fmt.pix.field) {
+ case V4L2_FIELD_INTERLACED:
+ pcdev->is_interlace = 1;
+ break;
+ case V4L2_FIELD_ANY:
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ /* fall-through */
+ case V4L2_FIELD_NONE:
+ pcdev->is_interlace = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
}
static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf,
@@ -532,7 +737,7 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q,
&sh_mobile_ceu_videobuf_ops,
&ici->dev, &pcdev->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_NONE,
+ V4L2_FIELD_ANY,
sizeof(struct sh_mobile_ceu_buffer),
icd);
}
@@ -541,12 +746,12 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
.owner = THIS_MODULE,
.add = sh_mobile_ceu_add_device,
.remove = sh_mobile_ceu_remove_device,
- .set_fmt_cap = sh_mobile_ceu_set_fmt_cap,
- .try_fmt_cap = sh_mobile_ceu_try_fmt_cap,
+ .get_formats = sh_mobile_ceu_get_formats,
+ .set_fmt = sh_mobile_ceu_set_fmt,
+ .try_fmt = sh_mobile_ceu_try_fmt,
.reqbufs = sh_mobile_ceu_reqbufs,
.poll = sh_mobile_ceu_poll,
.querycap = sh_mobile_ceu_querycap,
- .try_bus_param = sh_mobile_ceu_try_bus_param,
.set_bus_param = sh_mobile_ceu_set_bus_param,
.init_videobuf = sh_mobile_ceu_init_videobuf,
};
@@ -616,7 +821,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
/* request irq */
err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED,
- pdev->dev.bus_id, pcdev);
+ dev_name(&pdev->dev), pcdev);
if (err) {
dev_err(&pdev->dev, "Unable to register CEU interrupt.\n");
goto exit_release_mem;
@@ -633,8 +838,8 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
pcdev->ici.priv = pcdev;
pcdev->ici.dev.parent = &pdev->dev;
pcdev->ici.nr = pdev->id;
- pcdev->ici.drv_name = pdev->dev.bus_id,
- pcdev->ici.ops = &sh_mobile_ceu_host_ops,
+ pcdev->ici.drv_name = dev_name(&pdev->dev);
+ pcdev->ici.ops = &sh_mobile_ceu_host_ops;
err = soc_camera_host_register(&pcdev->ici);
if (err)
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
index fcd2b62f92c4..01a8efb8deb1 100644
--- a/drivers/media/video/sn9c102/sn9c102_core.c
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -2162,7 +2162,7 @@ sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg)
strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
- strlcpy(cap.bus_info, cam->usbdev->dev.bus_id,
+ strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev),
sizeof(cap.bus_info));
if (copy_to_user(arg, &cap, sizeof(cap)))
diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h
index e23734f6d6e2..8cb3457e778d 100644
--- a/drivers/media/video/sn9c102/sn9c102_devtable.h
+++ b/drivers/media/video/sn9c102/sn9c102_devtable.h
@@ -55,7 +55,9 @@ static const struct usb_device_id sn9c102_id_table[] = {
{ SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), },
+#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
{ SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), },
+#endif
/* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */
{ SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), },
@@ -91,10 +93,14 @@ static const struct usb_device_id sn9c102_id_table[] = {
{ SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), },
/* SN9C105 */
+#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
{ SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), },
+#endif
{ SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), },
+#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
{ SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), },
+#endif
{ SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), },
@@ -113,7 +119,9 @@ static const struct usb_device_id sn9c102_id_table[] = {
{ SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), },
{ SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), },
/* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */
+#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
{ SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), },
+#endif
{ SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), },
{ SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), },
{ SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), },
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 66ebe5956a87..90077cb4fe66 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -33,10 +33,9 @@
static LIST_HEAD(hosts);
static LIST_HEAD(devices);
static DEFINE_MUTEX(list_lock);
-static DEFINE_MUTEX(video_lock);
-const static struct soc_camera_data_format*
-format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc)
+const struct soc_camera_data_format *soc_camera_format_by_fourcc(
+ struct soc_camera_device *icd, unsigned int fourcc)
{
unsigned int i;
@@ -45,67 +44,87 @@ format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc)
return icd->formats + i;
return NULL;
}
+EXPORT_SYMBOL(soc_camera_format_by_fourcc);
-static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
+ struct soc_camera_device *icd, unsigned int fourcc)
{
- struct soc_camera_file *icf = file->private_data;
- struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
- enum v4l2_field field;
- const struct soc_camera_data_format *fmt;
- int ret;
+ unsigned int i;
- WARN_ON(priv != file->private_data);
+ for (i = 0; i < icd->num_user_formats; i++)
+ if (icd->user_formats[i].host_fmt->fourcc == fourcc)
+ return icd->user_formats + i;
+ return NULL;
+}
+EXPORT_SYMBOL(soc_camera_xlate_by_fourcc);
- fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat);
- if (!fmt) {
- dev_dbg(&icd->dev, "invalid format 0x%08x\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
+/**
+ * soc_camera_apply_sensor_flags() - apply platform SOCAM_SENSOR_INVERT_* flags
+ * @icl: camera platform parameters
+ * @flags: flags to be inverted according to platform configuration
+ * @return: resulting flags
+ */
+unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl,
+ unsigned long flags)
+{
+ unsigned long f;
- dev_dbg(&icd->dev, "fmt: 0x%08x\n", fmt->fourcc);
+ /* If only one of the two polarities is supported, switch to the opposite */
+ if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) {
+ f = flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW);
+ if (f == SOCAM_HSYNC_ACTIVE_HIGH || f == SOCAM_HSYNC_ACTIVE_LOW)
+ flags ^= SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW;
+ }
- field = f->fmt.pix.field;
+ if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) {
+ f = flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW);
+ if (f == SOCAM_VSYNC_ACTIVE_HIGH || f == SOCAM_VSYNC_ACTIVE_LOW)
+ flags ^= SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW;
+ }
- if (field == V4L2_FIELD_ANY) {
- field = V4L2_FIELD_NONE;
- } else if (V4L2_FIELD_NONE != field) {
- dev_err(&icd->dev, "Field type invalid.\n");
- return -EINVAL;
+ if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) {
+ f = flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING);
+ if (f == SOCAM_PCLK_SAMPLE_RISING || f == SOCAM_PCLK_SAMPLE_FALLING)
+ flags ^= SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING;
}
- /* test physical bus parameters */
- ret = ici->ops->try_bus_param(icd, f->fmt.pix.pixelformat);
- if (ret)
- return ret;
+ return flags;
+}
+EXPORT_SYMBOL(soc_camera_apply_sensor_flags);
- /* limit format to hardware capabilities */
- ret = ici->ops->try_fmt_cap(icd, f);
+static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct soc_camera_file *icf = file->private_data;
+ struct soc_camera_device *icd = icf->icd;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- /* calculate missing fields */
- f->fmt.pix.field = field;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
+ WARN_ON(priv != file->private_data);
- return ret;
+ /* limit format to hardware capabilities */
+ return ici->ops->try_fmt(icd, f);
}
static int soc_camera_enum_input(struct file *file, void *priv,
struct v4l2_input *inp)
{
+ struct soc_camera_file *icf = file->private_data;
+ struct soc_camera_device *icd = icf->icd;
+ int ret = 0;
+
if (inp->index != 0)
return -EINVAL;
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = V4L2_STD_UNKNOWN;
- strcpy(inp->name, "Camera");
+ if (icd->ops->enum_input)
+ ret = icd->ops->enum_input(icd, inp);
+ else {
+ /* default is camera */
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_UNKNOWN;
+ strcpy(inp->name, "Camera");
+ }
- return 0;
+ return ret;
}
static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i)
@@ -125,7 +144,14 @@ static int soc_camera_s_input(struct file *file, void *priv, unsigned int i)
static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
{
- return 0;
+ struct soc_camera_file *icf = file->private_data;
+ struct soc_camera_device *icd = icf->icd;
+ int ret = 0;
+
+ if (icd->ops->set_std)
+ ret = icd->ops->set_std(icd, a);
+
+ return ret;
}
static int soc_camera_reqbufs(struct file *file, void *priv,
@@ -134,8 +160,7 @@ static int soc_camera_reqbufs(struct file *file, void *priv,
int ret;
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
WARN_ON(priv != file->private_data);
@@ -178,6 +203,59 @@ static int soc_camera_dqbuf(struct file *file, void *priv,
return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK);
}
+static int soc_camera_init_user_formats(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ int i, fmts = 0;
+
+ if (!ici->ops->get_formats)
+ /*
+ * Fallback mode - the host will have to serve all
+ * sensor-provided formats one-to-one to the user
+ */
+ fmts = icd->num_formats;
+ else
+ /*
+ * First pass - only count formats this host-sensor
+ * configuration can provide
+ */
+ for (i = 0; i < icd->num_formats; i++)
+ fmts += ici->ops->get_formats(icd, i, NULL);
+
+ if (!fmts)
+ return -ENXIO;
+
+ icd->user_formats =
+ vmalloc(fmts * sizeof(struct soc_camera_format_xlate));
+ if (!icd->user_formats)
+ return -ENOMEM;
+
+ icd->num_user_formats = fmts;
+ fmts = 0;
+
+ dev_dbg(&icd->dev, "Found %d supported formats.\n", fmts);
+
+ /* Second pass - actually fill data formats */
+ for (i = 0; i < icd->num_formats; i++)
+ if (!ici->ops->get_formats) {
+ icd->user_formats[i].host_fmt = icd->formats + i;
+ icd->user_formats[i].cam_fmt = icd->formats + i;
+ icd->user_formats[i].buswidth = icd->formats[i].depth;
+ } else {
+ fmts += ici->ops->get_formats(icd, i,
+ &icd->user_formats[fmts]);
+ }
+
+ icd->current_fmt = icd->user_formats[0].host_fmt;
+
+ return 0;
+}
+
+static void soc_camera_free_user_formats(struct soc_camera_device *icd)
+{
+ vfree(icd->user_formats);
+}
+
static int soc_camera_open(struct inode *inode, struct file *file)
{
struct video_device *vdev;
@@ -190,8 +268,10 @@ static int soc_camera_open(struct inode *inode, struct file *file)
if (!icf)
return -ENOMEM;
- /* Protect against icd->remove() until we module_get() both drivers. */
- mutex_lock(&video_lock);
+ /*
+ * It is safe to dereference these pointers now as long as a user has
+ * the video device open - we are protected by the held cdev reference.
+ */
vdev = video_devdata(file);
icd = container_of(vdev->parent, struct soc_camera_device, dev);
@@ -209,20 +289,25 @@ static int soc_camera_open(struct inode *inode, struct file *file)
goto emgi;
}
+ /* Protect against icd->remove() until we module_get() both drivers. */
+ mutex_lock(&icd->video_lock);
+
icf->icd = icd;
icd->use_count++;
/* Now we really have to activate the camera */
if (icd->use_count == 1) {
+ ret = soc_camera_init_user_formats(icd);
+ if (ret < 0)
+ goto eiufmt;
ret = ici->ops->add(icd);
if (ret < 0) {
dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret);
- icd->use_count--;
goto eiciadd;
}
}
- mutex_unlock(&video_lock);
+ mutex_unlock(&icd->video_lock);
file->private_data = icf;
dev_dbg(&icd->dev, "camera device open\n");
@@ -231,13 +316,16 @@ static int soc_camera_open(struct inode *inode, struct file *file)
return 0;
- /* All errors are entered with the video_lock held */
+ /* First two errors are entered with the .video_lock held */
eiciadd:
+ soc_camera_free_user_formats(icd);
+eiufmt:
+ icd->use_count--;
+ mutex_unlock(&icd->video_lock);
module_put(ici->ops->owner);
emgi:
module_put(icd->ops->owner);
emgd:
- mutex_unlock(&video_lock);
vfree(icf);
return ret;
}
@@ -249,13 +337,16 @@ static int soc_camera_close(struct inode *inode, struct file *file)
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct video_device *vdev = icd->vdev;
- mutex_lock(&video_lock);
+ mutex_lock(&icd->video_lock);
icd->use_count--;
- if (!icd->use_count)
+ if (!icd->use_count) {
ici->ops->remove(icd);
+ soc_camera_free_user_formats(icd);
+ }
+ mutex_unlock(&icd->video_lock);
+
module_put(icd->ops->owner);
module_put(ici->ops->owner);
- mutex_unlock(&video_lock);
vfree(icf);
@@ -265,7 +356,7 @@ static int soc_camera_close(struct inode *inode, struct file *file)
}
static ssize_t soc_camera_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
@@ -299,8 +390,7 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
if (list_empty(&icf->vb_vidq.stream)) {
dev_err(&icd->dev, "Trying to poll with no queued buffers!\n");
@@ -310,7 +400,6 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
return ici->ops->poll(file, pt);
}
-
static struct file_operations soc_camera_fops = {
.owner = THIS_MODULE,
.open = soc_camera_open,
@@ -322,44 +411,50 @@ static struct file_operations soc_camera_fops = {
.llseek = no_llseek,
};
-
static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+ struct v4l2_format *f)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ __u32 pixfmt = pix->pixelformat;
int ret;
struct v4l2_rect rect;
- const static struct soc_camera_data_format *data_fmt;
WARN_ON(priv != file->private_data);
- data_fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat);
- if (!data_fmt)
- return -EINVAL;
-
- /* buswidth may be further adjusted by the ici */
- icd->buswidth = data_fmt->depth;
-
- ret = soc_camera_try_fmt_vid_cap(file, icf, f);
+ ret = soc_camera_try_fmt_vid_cap(file, priv, f);
if (ret < 0)
return ret;
+ mutex_lock(&icf->vb_vidq.vb_lock);
+
+ if (videobuf_queue_is_busy(&icf->vb_vidq)) {
+ dev_err(&icd->dev, "S_FMT denied: queue busy\n");
+ ret = -EBUSY;
+ goto unlock;
+ }
+
rect.left = icd->x_current;
rect.top = icd->y_current;
- rect.width = f->fmt.pix.width;
- rect.height = f->fmt.pix.height;
- ret = ici->ops->set_fmt_cap(icd, f->fmt.pix.pixelformat, &rect);
- if (ret < 0)
- return ret;
+ rect.width = pix->width;
+ rect.height = pix->height;
+ ret = ici->ops->set_fmt(icd, pix->pixelformat, &rect);
+ if (ret < 0) {
+ goto unlock;
+ } else if (!icd->current_fmt ||
+ icd->current_fmt->fourcc != pixfmt) {
+ dev_err(&ici->dev,
+ "Host driver hasn't set up current format correctly!\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
- icd->current_fmt = data_fmt;
icd->width = rect.width;
icd->height = rect.height;
- icf->vb_vidq.field = f->fmt.pix.field;
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type)
+ icf->vb_vidq.field = pix->field;
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n",
f->type);
@@ -367,11 +462,16 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
icd->width, icd->height);
/* set physical bus parameters */
- return ici->ops->set_bus_param(icd, f->fmt.pix.pixelformat);
+ ret = ici->ops->set_bus_param(icd, pixfmt);
+
+unlock:
+ mutex_unlock(&icf->vb_vidq.vb_lock);
+
+ return ret;
}
static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
+ struct v4l2_fmtdesc *f)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
@@ -379,10 +479,10 @@ static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv,
WARN_ON(priv != file->private_data);
- if (f->index >= icd->num_formats)
+ if (f->index >= icd->num_user_formats)
return -EINVAL;
- format = &icd->formats[f->index];
+ format = icd->user_formats[f->index].host_fmt;
strlcpy(f->description, format->name, sizeof(f->description));
f->pixelformat = format->fourcc;
@@ -390,21 +490,21 @@ static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv,
}
static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+ struct v4l2_format *f)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
WARN_ON(priv != file->private_data);
- f->fmt.pix.width = icd->width;
- f->fmt.pix.height = icd->height;
- f->fmt.pix.field = icf->vb_vidq.field;
- f->fmt.pix.pixelformat = icd->current_fmt->fourcc;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * icd->current_fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
+ pix->width = icd->width;
+ pix->height = icd->height;
+ pix->field = icf->vb_vidq.field;
+ pix->pixelformat = icd->current_fmt->fourcc;
+ pix->bytesperline = pix->width *
+ DIV_ROUND_UP(icd->current_fmt->depth, 8);
+ pix->sizeimage = pix->height * pix->bytesperline;
dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n",
icd->current_fmt->fourcc);
return 0;
@@ -415,8 +515,7 @@ static int soc_camera_querycap(struct file *file, void *priv,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
WARN_ON(priv != file->private_data);
@@ -429,6 +528,7 @@ static int soc_camera_streamon(struct file *file, void *priv,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ int ret;
WARN_ON(priv != file->private_data);
@@ -437,10 +537,16 @@ static int soc_camera_streamon(struct file *file, void *priv,
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
+ mutex_lock(&icd->video_lock);
+
icd->ops->start_capture(icd);
/* This calls buf_queue from host driver's videobuf_queue_ops */
- return videobuf_streamon(&icf->vb_vidq);
+ ret = videobuf_streamon(&icf->vb_vidq);
+
+ mutex_unlock(&icd->video_lock);
+
+ return ret;
}
static int soc_camera_streamoff(struct file *file, void *priv,
@@ -456,12 +562,16 @@ static int soc_camera_streamoff(struct file *file, void *priv,
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
+ mutex_lock(&icd->video_lock);
+
/* This calls buf_release from host driver's videobuf_queue_ops for all
* remaining buffers. When the last buffer is freed, stop capture */
videobuf_streamoff(&icf->vb_vidq);
icd->ops->stop_capture(icd);
+ mutex_unlock(&icd->video_lock);
+
return 0;
}
@@ -567,14 +677,16 @@ static int soc_camera_s_crop(struct file *file, void *fh,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
int ret;
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- ret = ici->ops->set_fmt_cap(icd, 0, &a->c);
+ /* Cropping is allowed during a running capture, guard consistency */
+ mutex_lock(&icf->vb_vidq.vb_lock);
+
+ ret = ici->ops->set_fmt(icd, 0, &a->c);
if (!ret) {
icd->width = a->c.width;
icd->height = a->c.height;
@@ -582,6 +694,8 @@ static int soc_camera_s_crop(struct file *file, void *fh,
icd->y_current = a->c.top;
}
+ mutex_unlock(&icf->vb_vidq.vb_lock);
+
return ret;
}
@@ -692,18 +806,35 @@ static int scan_add_device(struct soc_camera_device *icd)
static int soc_camera_probe(struct device *dev)
{
struct soc_camera_device *icd = to_soc_camera_dev(dev);
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
int ret;
- if (!icd->ops->probe)
- return -ENODEV;
+ /*
+ * Possible race scenario:
+ * modprobe <camera-host-driver> triggers __func__
+ * at this moment respective <camera-sensor-driver> gets rmmod'ed
+ * to protect take module references.
+ */
+
+ if (!try_module_get(icd->ops->owner)) {
+ dev_err(&icd->dev, "Couldn't lock sensor driver.\n");
+ ret = -EINVAL;
+ goto emgd;
+ }
+
+ if (!try_module_get(ici->ops->owner)) {
+ dev_err(&icd->dev, "Couldn't lock capture bus driver.\n");
+ ret = -EINVAL;
+ goto emgi;
+ }
+
+ mutex_lock(&icd->video_lock);
/* We only call ->add() here to activate and probe the camera.
* We shall ->remove() and deactivate it immediately afterwards. */
ret = ici->ops->add(icd);
if (ret < 0)
- return ret;
+ goto eiadd;
ret = icd->ops->probe(icd);
if (ret >= 0) {
@@ -717,6 +848,12 @@ static int soc_camera_probe(struct device *dev)
}
ici->ops->remove(icd);
+eiadd:
+ mutex_unlock(&icd->video_lock);
+ module_put(ici->ops->owner);
+emgi:
+ module_put(icd->ops->owner);
+emgd:
return ret;
}
@@ -779,11 +916,20 @@ int soc_camera_host_register(struct soc_camera_host *ici)
int ret;
struct soc_camera_host *ix;
- if (!ici->ops->init_videobuf || !ici->ops->add || !ici->ops->remove)
+ if (!ici || !ici->ops ||
+ !ici->ops->try_fmt ||
+ !ici->ops->set_fmt ||
+ !ici->ops->set_bus_param ||
+ !ici->ops->querycap ||
+ !ici->ops->init_videobuf ||
+ !ici->ops->reqbufs ||
+ !ici->ops->add ||
+ !ici->ops->remove ||
+ !ici->ops->poll)
return -EINVAL;
/* Number might be equal to the platform device ID */
- sprintf(ici->dev.bus_id, "camera_host%d", ici->nr);
+ dev_set_name(&ici->dev, "camera_host%d", ici->nr);
mutex_lock(&list_lock);
list_for_each_entry(ix, &hosts, list) {
@@ -847,7 +993,16 @@ int soc_camera_device_register(struct soc_camera_device *icd)
struct soc_camera_device *ix;
int num = -1, i;
- if (!icd)
+ if (!icd || !icd->ops ||
+ !icd->ops->probe ||
+ !icd->ops->init ||
+ !icd->ops->release ||
+ !icd->ops->start_capture ||
+ !icd->ops->stop_capture ||
+ !icd->ops->set_fmt ||
+ !icd->ops->try_fmt ||
+ !icd->ops->query_bus_param ||
+ !icd->ops->set_bus_param)
return -EINVAL;
for (i = 0; i < 256 && num < 0; i++) {
@@ -867,10 +1022,12 @@ int soc_camera_device_register(struct soc_camera_device *icd)
icd->devnum = num;
icd->dev.bus = &soc_camera_bus_type;
- snprintf(icd->dev.bus_id, sizeof(icd->dev.bus_id),
- "%u-%u", icd->iface, icd->devnum);
+ dev_set_name(&icd->dev, "%u-%u", icd->iface, icd->devnum);
- icd->dev.release = dummy_release;
+ icd->dev.release = dummy_release;
+ icd->use_count = 0;
+ icd->host_priv = NULL;
+ mutex_init(&icd->video_lock);
return scan_add_device(icd);
}
@@ -917,6 +1074,10 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
#endif
};
+/*
+ * Usually called from the struct soc_camera_ops .probe() method, i.e., from
+ * soc_camera_probe() above with .video_lock held
+ */
int soc_camera_video_start(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
@@ -932,7 +1093,7 @@ int soc_camera_video_start(struct soc_camera_device *icd)
dev_dbg(&ici->dev, "Allocated video_device %p\n", vdev);
strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
- /* Maybe better &ici->dev */
+
vdev->parent = &icd->dev;
vdev->current_norm = V4L2_STD_UNKNOWN;
vdev->fops = &soc_camera_fops;
@@ -941,8 +1102,6 @@ int soc_camera_video_start(struct soc_camera_device *icd)
vdev->minor = -1;
vdev->tvnorms = V4L2_STD_UNKNOWN,
- icd->current_fmt = &icd->formats[0];
-
err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor);
if (err < 0) {
dev_err(vdev->parent, "video_register_device failed\n");
@@ -968,10 +1127,10 @@ void soc_camera_video_stop(struct soc_camera_device *icd)
if (!icd->dev.parent || !vdev)
return;
- mutex_lock(&video_lock);
+ mutex_lock(&icd->video_lock);
video_unregister_device(vdev);
icd->vdev = NULL;
- mutex_unlock(&video_lock);
+ mutex_unlock(&icd->video_lock);
}
EXPORT_SYMBOL(soc_camera_video_stop);
diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c
index bb7a9d480e8f..013ab06e3180 100644
--- a/drivers/media/video/soc_camera_platform.c
+++ b/drivers/media/video/soc_camera_platform.c
@@ -79,19 +79,20 @@ soc_camera_platform_query_bus_param(struct soc_camera_device *icd)
return p->bus_param;
}
-static int soc_camera_platform_set_fmt_cap(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect)
+static int soc_camera_platform_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
{
return 0;
}
-static int soc_camera_platform_try_fmt_cap(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int soc_camera_platform_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
{
struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
- f->fmt.pix.width = p->format.width;
- f->fmt.pix.height = p->format.height;
+ pix->width = p->format.width;
+ pix->height = p->format.height;
return 0;
}
@@ -124,8 +125,8 @@ static struct soc_camera_ops soc_camera_platform_ops = {
.release = soc_camera_platform_release,
.start_capture = soc_camera_platform_start_capture,
.stop_capture = soc_camera_platform_stop_capture,
- .set_fmt_cap = soc_camera_platform_set_fmt_cap,
- .try_fmt_cap = soc_camera_platform_try_fmt_cap,
+ .set_fmt = soc_camera_platform_set_fmt,
+ .try_fmt = soc_camera_platform_try_fmt,
.set_bus_param = soc_camera_platform_set_bus_param,
.query_bus_param = soc_camera_platform_query_bus_param,
};
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
index e9eb6d754d5c..f9516d0f3c11 100644
--- a/drivers/media/video/stk-webcam.c
+++ b/drivers/media/video/stk-webcam.c
@@ -1262,6 +1262,25 @@ static int stk_vidioc_g_parm(struct file *filp,
return 0;
}
+static int stk_vidioc_enum_framesizes(struct file *filp,
+ void *priv, struct v4l2_frmsizeenum *frms)
+{
+ if (frms->index >= ARRAY_SIZE(stk_sizes))
+ return -EINVAL;
+ switch (frms->pixel_format) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_SBGGR8:
+ frms->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ frms->discrete.width = stk_sizes[frms->index].w;
+ frms->discrete.height = stk_sizes[frms->index].h;
+ return 0;
+ default: return -EINVAL;
+ }
+}
+
static struct file_operations v4l_stk_fops = {
.owner = THIS_MODULE,
.open = v4l_stk_open,
@@ -1296,6 +1315,7 @@ static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = {
.vidioc_g_ctrl = stk_vidioc_g_ctrl,
.vidioc_s_ctrl = stk_vidioc_s_ctrl,
.vidioc_g_parm = stk_vidioc_g_parm,
+ .vidioc_enum_framesizes = stk_vidioc_enum_framesizes,
};
static void stk_v4l_dev_release(struct video_device *vd)
@@ -1376,12 +1396,9 @@ static int stk_camera_probe(struct usb_interface *interface,
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->isoc_ep
- && ((endpoint->bEndpointAddress
- & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
- && ((endpoint->bmAttributes
- & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC)) {
+ && usb_endpoint_is_isoc_in(endpoint)) {
/* we found an isoc in endpoint */
- dev->isoc_ep = (endpoint->bEndpointAddress & 0xF);
+ dev->isoc_ep = usb_endpoint_num(endpoint);
break;
}
}
diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c
index 328c41b1517d..42acc92c182d 100644
--- a/drivers/media/video/stv680.c
+++ b/drivers/media/video/stv680.c
@@ -1132,8 +1132,7 @@ static int stv_close (struct inode *inode, struct file *file)
return 0;
}
-static int stv680_do_ioctl (struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int stv680_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = file->private_data;
struct usb_stv *stv680 = video_get_drvdata(vdev);
@@ -1303,7 +1302,7 @@ static int stv680_do_ioctl (struct inode *inode, struct file *file,
static int stv680_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, stv680_do_ioctl);
+ return video_usercopy(file, cmd, arg, stv680_do_ioctl);
}
static int stv680_mmap (struct file *file, struct vm_area_struct *vma)
diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c
index 4963d4264880..0c020585fffb 100644
--- a/drivers/media/video/tda7432.c
+++ b/drivers/media/video/tda7432.c
@@ -47,9 +47,10 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/i2c-addr.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#ifndef VIDEO_AUDIO_BALANCE
# define VIDEO_AUDIO_BALANCE 32
@@ -79,6 +80,7 @@ I2C_CLIENT_INSMOD;
/* Structure of address and subaddresses for the tda7432 */
struct tda7432 {
+ struct v4l2_subdev sd;
int addr;
int input;
int volume;
@@ -86,10 +88,12 @@ struct tda7432 {
int bass, treble;
int lf, lr, rf, rr;
int loud;
- struct i2c_client c;
};
-static struct i2c_driver driver;
-static struct i2c_client client_template;
+
+static inline struct tda7432 *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tda7432, sd);
+}
/* The TDA7432 is made by STS-Thompson
* http://www.st.com
@@ -224,32 +228,33 @@ static struct i2c_client client_template;
/* Begin code */
-static int tda7432_write(struct i2c_client *client, int subaddr, int val)
+static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
- v4l_dbg(2, debug,client,"In tda7432_write\n");
- v4l_dbg(1, debug,client,"Writing %d 0x%x\n", subaddr, val);
+
+ v4l2_dbg(2, debug, sd, "In tda7432_write\n");
+ v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val);
buffer[0] = subaddr;
buffer[1] = val;
- if (2 != i2c_master_send(client,buffer,2)) {
- v4l_err(client,"I/O error, trying (write %d 0x%x)\n",
+ if (2 != i2c_master_send(client, buffer, 2)) {
+ v4l2_err(sd, "I/O error, trying (write %d 0x%x)\n",
subaddr, val);
return -1;
}
return 0;
}
-/* I don't think we ever actually _read_ the chip... */
-
-static int tda7432_set(struct i2c_client *client)
+static int tda7432_set(struct v4l2_subdev *sd)
{
- struct tda7432 *t = i2c_get_clientdata(client);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tda7432 *t = to_state(sd);
unsigned char buf[16];
- v4l_dbg(2, debug,client,"In tda7432_set\n");
- v4l_dbg(1, debug,client,
+ v4l2_dbg(1, debug, sd,
"tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n",
- t->input,t->volume,t->bass,t->treble,t->lf,t->lr,t->rf,t->rr,t->loud);
+ t->input, t->volume, t->bass, t->treble, t->lf, t->lr,
+ t->rf, t->rr, t->loud);
buf[0] = TDA7432_IN;
buf[1] = t->input;
buf[2] = t->volume;
@@ -260,18 +265,19 @@ static int tda7432_set(struct i2c_client *client)
buf[7] = t->rf;
buf[8] = t->rr;
buf[9] = t->loud;
- if (10 != i2c_master_send(client,buf,10)) {
- v4l_err(client,"I/O error, trying tda7432_set\n");
+ if (10 != i2c_master_send(client, buf, 10)) {
+ v4l2_err(sd, "I/O error, trying tda7432_set\n");
return -1;
}
return 0;
}
-static void do_tda7432_init(struct i2c_client *client)
+static void do_tda7432_init(struct v4l2_subdev *sd)
{
- struct tda7432 *t = i2c_get_clientdata(client);
- v4l_dbg(2, debug,client,"In tda7432_init\n");
+ struct tda7432 *t = to_state(sd);
+
+ v4l2_dbg(2, debug, sd, "In tda7432_init\n");
t->input = TDA7432_STEREO_IN | /* Main (stereo) input */
TDA7432_BASS_SYM | /* Symmetric bass cut */
@@ -288,57 +294,12 @@ static void do_tda7432_init(struct i2c_client *client)
t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */
t->loud = loudness; /* insmod parameter */
- tda7432_set(client);
+ tda7432_set(sd);
}
-/* *********************** *
- * i2c interface functions *
- * *********************** */
-
-static int tda7432_attach(struct i2c_adapter *adap, int addr, int kind)
+static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct tda7432 *t;
- struct i2c_client *client;
-
- t = kzalloc(sizeof *t,GFP_KERNEL);
- if (!t)
- return -ENOMEM;
-
- client = &t->c;
- memcpy(client,&client_template,sizeof(struct i2c_client));
- client->adapter = adap;
- client->addr = addr;
- i2c_set_clientdata(client, t);
-
- do_tda7432_init(client);
- i2c_attach_client(client);
-
- v4l_info(client, "chip found @ 0x%x (%s)\n", addr << 1, adap->name);
- return 0;
-}
-
-static int tda7432_probe(struct i2c_adapter *adap)
-{
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, tda7432_attach);
- return 0;
-}
-
-static int tda7432_detach(struct i2c_client *client)
-{
- struct tda7432 *t = i2c_get_clientdata(client);
-
- do_tda7432_init(client);
- i2c_detach_client(client);
-
- kfree(t);
- return 0;
-}
-
-static int tda7432_get_ctrl(struct i2c_client *client,
- struct v4l2_control *ctrl)
-{
- struct tda7432 *t = i2c_get_clientdata(client);
+ struct tda7432 *t = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -382,10 +343,9 @@ static int tda7432_get_ctrl(struct i2c_client *client,
return -EINVAL;
}
-static int tda7432_set_ctrl(struct i2c_client *client,
- struct v4l2_control *ctrl)
+static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct tda7432 *t = i2c_get_clientdata(client);
+ struct tda7432 *t = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -400,7 +360,7 @@ static int tda7432_set_ctrl(struct i2c_client *client,
if (loudness) /* Turn on the loudness bit */
t->volume |= TDA7432_LD_ON;
- tda7432_write(client,TDA7432_VL, t->volume);
+ tda7432_write(sd, TDA7432_VL, t->volume);
return 0;
case V4L2_CID_AUDIO_BALANCE:
if (ctrl->value < 32768) {
@@ -428,14 +388,14 @@ static int tda7432_set_ctrl(struct i2c_client *client,
if(t->bass>= 0x8)
t->bass = (~t->bass & 0xf) + 0x8 ;
- tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble );
+ tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble);
return 0;
case V4L2_CID_AUDIO_TREBLE:
t->treble= ctrl->value >> 12;
if(t->treble>= 0x8)
t->treble = (~t->treble & 0xf) + 0x8 ;
- tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble );
+ tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble);
return 0;
default:
return -EINVAL;
@@ -445,92 +405,102 @@ static int tda7432_set_ctrl(struct i2c_client *client,
if (t->muted)
{
/* Mute & update balance*/
- tda7432_write(client,TDA7432_LF, t->lf | TDA7432_MUTE);
- tda7432_write(client,TDA7432_LR, t->lr | TDA7432_MUTE);
- tda7432_write(client,TDA7432_RF, t->rf | TDA7432_MUTE);
- tda7432_write(client,TDA7432_RR, t->rr | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE);
} else {
- tda7432_write(client,TDA7432_LF, t->lf);
- tda7432_write(client,TDA7432_LR, t->lr);
- tda7432_write(client,TDA7432_RF, t->rf);
- tda7432_write(client,TDA7432_RR, t->rr);
+ tda7432_write(sd, TDA7432_LF, t->lf);
+ tda7432_write(sd, TDA7432_LR, t->lr);
+ tda7432_write(sd, TDA7432_RF, t->rf);
+ tda7432_write(sd, TDA7432_RR, t->rr);
}
return 0;
}
-static int tda7432_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
- v4l_dbg(2, debug,client,"In tda7432_command\n");
- if (debug>1)
- v4l_i2c_print_ioctl(client,cmd);
-
- switch (cmd) {
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- default:
- return -EINVAL;
- }
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
return v4l2_ctrl_query_fill_std(qc);
}
- case VIDIOC_S_CTRL:
- return tda7432_set_ctrl(client, arg);
-
- case VIDIOC_G_CTRL:
- return tda7432_get_ctrl(client, arg);
-
- } /* end of (cmd) switch */
+ return -EINVAL;
+}
- return 0;
+static int tda7432_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
}
-static struct i2c_driver driver = {
- .driver = {
- .name = "tda7432",
- },
- .id = I2C_DRIVERID_TDA7432,
- .attach_adapter = tda7432_probe,
- .detach_client = tda7432_detach,
- .command = tda7432_command,
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tda7432_core_ops = {
+ .queryctrl = tda7432_queryctrl,
+ .g_ctrl = tda7432_g_ctrl,
+ .s_ctrl = tda7432_s_ctrl,
};
-static struct i2c_client client_template =
-{
- .name = "tda7432",
- .driver = &driver,
+static const struct v4l2_subdev_ops tda7432_ops = {
+ .core = &tda7432_core_ops,
};
-static int __init tda7432_init(void)
+/* ----------------------------------------------------------------------- */
+
+/* *********************** *
+ * i2c interface functions *
+ * *********************** */
+
+static int tda7432_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- if ( (loudness < 0) || (loudness > 15) ) {
- printk(KERN_ERR "loudness parameter must be between 0 and 15\n");
- return -EINVAL;
+ struct tda7432 *t;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+ sd = &t->sd;
+ v4l2_i2c_subdev_init(sd, client, &tda7432_ops);
+ if (loudness < 0 || loudness > 15) {
+ v4l2_warn(sd, "loudness parameter must be between 0 and 15\n");
+ if (loudness < 0)
+ loudness = 0;
+ if (loudness > 15)
+ loudness = 15;
}
- return i2c_add_driver(&driver);
+ do_tda7432_init(sd);
+ return 0;
}
-static void __exit tda7432_fini(void)
+static int tda7432_remove(struct i2c_client *client)
{
- i2c_del_driver(&driver);
-}
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
-module_init(tda7432_init);
-module_exit(tda7432_fini);
+ do_tda7432_init(sd);
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+static const struct i2c_device_id tda7432_id[] = {
+ { "tda7432", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tda7432_id);
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tda7432",
+ .driverid = I2C_DRIVERID_TDA7432,
+ .command = tda7432_command,
+ .probe = tda7432_probe,
+ .remove = tda7432_remove,
+ .id_table = tda7432_id,
+};
diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c
index 1c391f0328fd..2644e0dc9251 100644
--- a/drivers/media/video/tda9840.c
+++ b/drivers/media/video/tda9840.c
@@ -29,7 +29,7 @@
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "tda9840.h"
@@ -62,85 +62,89 @@ static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END };
/* magic definition of all other variables and things */
I2C_CLIENT_INSMOD;
-static void tda9840_write(struct i2c_client *client, u8 reg, u8 val)
+static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
if (i2c_smbus_write_byte_data(client, reg, val))
- v4l_dbg(1, debug, client, "error writing %02x to %02x\n",
+ v4l2_dbg(1, debug, sd, "error writing %02x to %02x\n",
val, reg);
}
-static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
{
- int byte = *(int *)arg;
-
- switch (cmd) {
- case VIDIOC_S_TUNER: {
- struct v4l2_tuner *t = arg;
- int byte;
+ int byte;
- if (t->index)
- return -EINVAL;
+ if (t->index)
+ return -EINVAL;
- switch (t->audmode) {
- case V4L2_TUNER_MODE_STEREO:
- byte = TDA9840_SET_STEREO;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- byte = TDA9840_SET_BOTH;
- break;
- case V4L2_TUNER_MODE_LANG1:
- byte = TDA9840_SET_LANG1;
- break;
- case V4L2_TUNER_MODE_LANG2:
- byte = TDA9840_SET_LANG2;
- break;
- default:
- byte = TDA9840_SET_MONO;
- break;
- }
- v4l_dbg(1, debug, client, "TDA9840_SWITCH: 0x%02x\n", byte);
- tda9840_write(client, SWITCH, byte);
+ switch (t->audmode) {
+ case V4L2_TUNER_MODE_STEREO:
+ byte = TDA9840_SET_STEREO;
break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ byte = TDA9840_SET_BOTH;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ byte = TDA9840_SET_LANG1;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ byte = TDA9840_SET_LANG2;
+ break;
+ default:
+ byte = TDA9840_SET_MONO;
+ break;
+ }
+ v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte);
+ tda9840_write(sd, SWITCH, byte);
+ return 0;
+}
+
+static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 byte;
+
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ if (1 != i2c_master_recv(client, &byte, 1)) {
+ v4l2_dbg(1, debug, sd,
+ "i2c_master_recv() failed\n");
+ return -EIO;
+ }
+
+ if (byte & 0x80) {
+ v4l2_dbg(1, debug, sd,
+ "TDA9840_DETECT: register contents invalid\n");
+ return -EINVAL;
}
- case VIDIOC_G_TUNER: {
- struct v4l2_tuner *t = arg;
- u8 byte;
+ v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte);
+ switch (byte & 0x60) {
+ case 0x00:
t->rxsubchans = V4L2_TUNER_SUB_MONO;
- if (1 != i2c_master_recv(client, &byte, 1)) {
- v4l_dbg(1, debug, client,
- "i2c_master_recv() failed\n");
- return -EIO;
- }
-
- if (byte & 0x80) {
- v4l_dbg(1, debug, client,
- "TDA9840_DETECT: register contents invalid\n");
- return -EINVAL;
- }
-
- v4l_dbg(1, debug, client, "TDA9840_DETECT: byte: 0x%02x\n", byte);
-
- switch (byte & 0x60) {
- case 0x00:
- t->rxsubchans = V4L2_TUNER_SUB_MONO;
- break;
- case 0x20:
- t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- break;
- case 0x40:
- t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
- break;
- default: /* Incorrect detect */
- t->rxsubchans = V4L2_TUNER_MODE_MONO;
- break;
- }
+ break;
+ case 0x20:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x40:
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
+ break;
+ default: /* Incorrect detect */
+ t->rxsubchans = V4L2_TUNER_MODE_MONO;
break;
}
+ return 0;
+}
+static int tda9840_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg)
+{
+ int byte;
+
+ switch (cmd) {
case TDA9840_LEVEL_ADJUST:
- v4l_dbg(1, debug, client, "TDA9840_LEVEL_ADJUST: %d\n", byte);
+ byte = *(int *)arg;
+ v4l2_dbg(1, debug, sd, "TDA9840_LEVEL_ADJUST: %d\n", byte);
/* check for correct range */
if (byte > 25 || byte < -20)
@@ -152,11 +156,12 @@ static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
byte += 0x8;
else
byte = -byte;
- tda9840_write(client, LEVEL_ADJUST, byte);
+ tda9840_write(sd, LEVEL_ADJUST, byte);
break;
case TDA9840_STEREO_ADJUST:
- v4l_dbg(1, debug, client, "TDA9840_STEREO_ADJUST: %d\n", byte);
+ byte = *(int *)arg;
+ v4l2_dbg(1, debug, sd, "TDA9840_STEREO_ADJUST: %d\n", byte);
/* check for correct range */
if (byte > 25 || byte < -24)
@@ -169,18 +174,41 @@ static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
else
byte = -byte;
- tda9840_write(client, STEREO_ADJUST, byte);
+ tda9840_write(sd, STEREO_ADJUST, byte);
break;
default:
return -ENOIOCTLCMD;
}
-
return 0;
}
+static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tda9840_core_ops = {
+ .ioctl = tda9840_ioctl,
+};
+
+static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = {
+ .s_tuner = tda9840_s_tuner,
+ .g_tuner = tda9840_g_tuner,
+};
+
+static const struct v4l2_subdev_ops tda9840_ops = {
+ .core = &tda9840_core_ops,
+ .tuner = &tda9840_tuner_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
static int tda9840_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
int result;
int byte;
@@ -188,23 +216,38 @@ static int tda9840_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA |
I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
- return 0;
+ return -EIO;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
+ sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tda9840_ops);
+
/* set initial values for level & stereo - adjustment, mode */
byte = 0;
- result = tda9840_command(client, TDA9840_LEVEL_ADJUST, &byte);
- result += tda9840_command(client, TDA9840_STEREO_ADJUST, &byte);
- tda9840_write(client, SWITCH, TDA9840_SET_STEREO);
+ result = tda9840_ioctl(sd, TDA9840_LEVEL_ADJUST, &byte);
+ result |= tda9840_ioctl(sd, TDA9840_STEREO_ADJUST, &byte);
+ tda9840_write(sd, SWITCH, TDA9840_SET_STEREO);
if (result) {
- v4l_dbg(1, debug, client, "could not initialize tda9840\n");
+ v4l2_dbg(1, debug, sd, "could not initialize tda9840\n");
+ kfree(sd);
return -ENODEV;
}
return 0;
}
+static int tda9840_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ return 0;
+}
+
static int tda9840_legacy_probe(struct i2c_adapter *adapter)
{
/* Let's see whether this is a known adapter we can attach to.
@@ -222,6 +265,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.driverid = I2C_DRIVERID_TDA9840,
.command = tda9840_command,
.probe = tda9840_probe,
+ .remove = tda9840_remove,
.legacy_probe = tda9840_legacy_probe,
.id_table = tda9840_id,
};
diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c
index 792f0b079909..56f0c0eb500f 100644
--- a/drivers/media/video/tda9875.c
+++ b/drivers/media/video/tda9875.c
@@ -25,11 +25,10 @@
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
#include <linux/i2c.h>
-#include <linux/init.h>
-
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include <media/i2c-addr.h>
static int debug; /* insmod parameter */
@@ -46,13 +45,15 @@ I2C_CLIENT_INSMOD;
/* This is a superset of the TDA9875 */
struct tda9875 {
+ struct v4l2_subdev sd;
int rvol, lvol;
int bass, treble;
- struct i2c_client c;
};
-static struct i2c_driver driver;
-static struct i2c_client client_template;
+static inline struct tda9875 *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tda9875, sd);
+}
#define dprintk if (debug) printk
@@ -105,15 +106,16 @@ static struct i2c_client client_template;
/* Begin code */
-static int tda9875_write(struct i2c_client *client, int subaddr, unsigned char val)
+static int tda9875_write(struct v4l2_subdev *sd, int subaddr, unsigned char val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
- dprintk("In tda9875_write\n");
- dprintk("Writing %d 0x%x\n", subaddr, val);
+
+ v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val);
buffer[0] = subaddr;
buffer[1] = val;
- if (2 != i2c_master_send(client,buffer,2)) {
- printk(KERN_WARNING "tda9875: I/O error, trying (write %d 0x%x)\n",
+ if (2 != i2c_master_send(client, buffer, 2)) {
+ v4l2_warn(sd, "I/O error, trying (write %d 0x%x)\n",
subaddr, val);
return -1;
}
@@ -121,7 +123,7 @@ static int tda9875_write(struct i2c_client *client, int subaddr, unsigned char v
}
-static int i2c_read_register(struct i2c_adapter *adap, int addr, int reg)
+static int i2c_read_register(struct i2c_client *client, int addr, int reg)
{
unsigned char write[1];
unsigned char read[1];
@@ -129,150 +131,83 @@ static int i2c_read_register(struct i2c_adapter *adap, int addr, int reg)
{ addr, 0, 1, write },
{ addr, I2C_M_RD, 1, read }
};
+
write[0] = reg;
- if (2 != i2c_transfer(adap,msgs,2)) {
- printk(KERN_WARNING "tda9875: I/O error (read2)\n");
+ if (2 != i2c_transfer(client->adapter, msgs, 2)) {
+ v4l_warn(client, "I/O error (read2)\n");
return -1;
}
- dprintk("tda9875: chip_read2: reg%d=0x%x\n",reg,read[0]);
+ v4l_dbg(1, debug, client, "chip_read2: reg%d=0x%x\n", reg, read[0]);
return read[0];
}
-static void tda9875_set(struct i2c_client *client)
+static void tda9875_set(struct v4l2_subdev *sd)
{
- struct tda9875 *tda = i2c_get_clientdata(client);
+ struct tda9875 *tda = to_state(sd);
unsigned char a;
- dprintk(KERN_DEBUG "tda9875_set(%04x,%04x,%04x,%04x)\n",
- tda->lvol,tda->rvol,tda->bass,tda->treble);
-
+ v4l2_dbg(1, debug, sd, "tda9875_set(%04x,%04x,%04x,%04x)\n",
+ tda->lvol, tda->rvol, tda->bass, tda->treble);
a = tda->lvol & 0xff;
- tda9875_write(client, TDA9875_MVL, a);
+ tda9875_write(sd, TDA9875_MVL, a);
a =tda->rvol & 0xff;
- tda9875_write(client, TDA9875_MVR, a);
+ tda9875_write(sd, TDA9875_MVR, a);
a =tda->bass & 0xff;
- tda9875_write(client, TDA9875_MBA, a);
+ tda9875_write(sd, TDA9875_MBA, a);
a =tda->treble & 0xff;
- tda9875_write(client, TDA9875_MTR, a);
+ tda9875_write(sd, TDA9875_MTR, a);
}
-static void do_tda9875_init(struct i2c_client *client)
+static void do_tda9875_init(struct v4l2_subdev *sd)
{
- struct tda9875 *t = i2c_get_clientdata(client);
- dprintk("In tda9875_init\n");
- tda9875_write(client, TDA9875_CFG, 0xd0 ); /*reg de config 0 (reset)*/
- tda9875_write(client, TDA9875_MSR, 0x03 ); /* Monitor 0b00000XXX*/
- tda9875_write(client, TDA9875_C1MSB, 0x00 ); /*Car1(FM) MSB XMHz*/
- tda9875_write(client, TDA9875_C1MIB, 0x00 ); /*Car1(FM) MIB XMHz*/
- tda9875_write(client, TDA9875_C1LSB, 0x00 ); /*Car1(FM) LSB XMHz*/
- tda9875_write(client, TDA9875_C2MSB, 0x00 ); /*Car2(NICAM) MSB XMHz*/
- tda9875_write(client, TDA9875_C2MIB, 0x00 ); /*Car2(NICAM) MIB XMHz*/
- tda9875_write(client, TDA9875_C2LSB, 0x00 ); /*Car2(NICAM) LSB XMHz*/
- tda9875_write(client, TDA9875_DCR, 0x00 ); /*Demod config 0x00*/
- tda9875_write(client, TDA9875_DEEM, 0x44 ); /*DE-Emph 0b0100 0100*/
- tda9875_write(client, TDA9875_FMAT, 0x00 ); /*FM Matrix reg 0x00*/
- tda9875_write(client, TDA9875_SC1, 0x00 ); /* SCART 1 (SC1)*/
- tda9875_write(client, TDA9875_SC2, 0x01 ); /* SCART 2 (sc2)*/
-
- tda9875_write(client, TDA9875_CH1V, 0x10 ); /* Channel volume 1 mute*/
- tda9875_write(client, TDA9875_CH2V, 0x10 ); /* Channel volume 2 mute */
- tda9875_write(client, TDA9875_DACOS, 0x02 ); /* sig DAC i/o(in:nicam)*/
- tda9875_write(client, TDA9875_ADCIS, 0x6f ); /* sig ADC input(in:mono)*/
- tda9875_write(client, TDA9875_LOSR, 0x00 ); /* line out (in:mono)*/
- tda9875_write(client, TDA9875_AER, 0x00 ); /*06 Effect (AVL+PSEUDO) */
- tda9875_write(client, TDA9875_MCS, 0x44 ); /* Main ch select (DAC) */
- tda9875_write(client, TDA9875_MVL, 0x03 ); /* Vol Main left 10dB */
- tda9875_write(client, TDA9875_MVR, 0x03 ); /* Vol Main right 10dB*/
- tda9875_write(client, TDA9875_MBA, 0x00 ); /* Main Bass Main 0dB*/
- tda9875_write(client, TDA9875_MTR, 0x00 ); /* Main Treble Main 0dB*/
- tda9875_write(client, TDA9875_ACS, 0x44 ); /* Aux chan select (dac)*/
- tda9875_write(client, TDA9875_AVL, 0x00 ); /* Vol Aux left 0dB*/
- tda9875_write(client, TDA9875_AVR, 0x00 ); /* Vol Aux right 0dB*/
- tda9875_write(client, TDA9875_ABA, 0x00 ); /* Aux Bass Main 0dB*/
- tda9875_write(client, TDA9875_ATR, 0x00 ); /* Aux Aigus Main 0dB*/
-
- tda9875_write(client, TDA9875_MUT, 0xcc ); /* General mute */
-
- t->lvol=t->rvol =0; /* 0dB */
- t->bass=0; /* 0dB */
- t->treble=0; /* 0dB */
- tda9875_set(client);
-
+ struct tda9875 *t = to_state(sd);
+
+ v4l2_dbg(1, debug, sd, "In tda9875_init\n");
+ tda9875_write(sd, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/
+ tda9875_write(sd, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/
+ tda9875_write(sd, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/
+ tda9875_write(sd, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/
+ tda9875_write(sd, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/
+ tda9875_write(sd, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/
+ tda9875_write(sd, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/
+ tda9875_write(sd, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/
+ tda9875_write(sd, TDA9875_DCR, 0x00); /*Demod config 0x00*/
+ tda9875_write(sd, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/
+ tda9875_write(sd, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/
+ tda9875_write(sd, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/
+ tda9875_write(sd, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/
+
+ tda9875_write(sd, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/
+ tda9875_write(sd, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */
+ tda9875_write(sd, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/
+ tda9875_write(sd, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/
+ tda9875_write(sd, TDA9875_LOSR, 0x00); /* line out (in:mono)*/
+ tda9875_write(sd, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */
+ tda9875_write(sd, TDA9875_MCS, 0x44); /* Main ch select (DAC) */
+ tda9875_write(sd, TDA9875_MVL, 0x03); /* Vol Main left 10dB */
+ tda9875_write(sd, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/
+ tda9875_write(sd, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/
+ tda9875_write(sd, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/
+ tda9875_write(sd, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/
+ tda9875_write(sd, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/
+ tda9875_write(sd, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/
+ tda9875_write(sd, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/
+ tda9875_write(sd, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/
+
+ tda9875_write(sd, TDA9875_MUT, 0xcc); /* General mute */
+
+ t->lvol = t->rvol = 0; /* 0dB */
+ t->bass = 0; /* 0dB */
+ t->treble = 0; /* 0dB */
+ tda9875_set(sd);
}
-/* *********************** *
- * i2c interface functions *
- * *********************** */
-
-static int tda9875_checkit(struct i2c_adapter *adap, int addr)
-{
- int dic,rev;
-
- dic=i2c_read_register(adap,addr,254);
- rev=i2c_read_register(adap,addr,255);
-
- if(dic==0 || dic==2) { // tda9875 and tda9875A
- printk("tda9875: TDA9875%s Rev.%d detected at 0x%x\n",
- dic==0?"":"A", rev,addr<<1);
- return 1;
- }
- printk("tda9875: no such chip at 0x%x (dic=0x%x rev=0x%x)\n",addr<<1,dic,rev);
- return(0);
-}
-
-static int tda9875_attach(struct i2c_adapter *adap, int addr, int kind)
+static int tda9875_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct tda9875 *t;
- struct i2c_client *client;
- dprintk("In tda9875_attach\n");
-
- t = kzalloc(sizeof *t,GFP_KERNEL);
- if (!t)
- return -ENOMEM;
-
- client = &t->c;
- memcpy(client,&client_template,sizeof(struct i2c_client));
- client->adapter = adap;
- client->addr = addr;
- i2c_set_clientdata(client, t);
-
- if(!tda9875_checkit(adap,addr)) {
- kfree(t);
- return 1;
- }
-
- do_tda9875_init(client);
- printk(KERN_INFO "tda9875: init\n");
-
- i2c_attach_client(client);
- return 0;
-}
-
-static int tda9875_probe(struct i2c_adapter *adap)
-{
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, tda9875_attach);
- return 0;
-}
-
-static int tda9875_detach(struct i2c_client *client)
-{
- struct tda9875 *t = i2c_get_clientdata(client);
-
- do_tda9875_init(client);
- i2c_detach_client(client);
-
- kfree(t);
- return 0;
-}
-
-static int tda9875_get_ctrl(struct i2c_client *client,
- struct v4l2_control *ctrl)
-{
- struct tda9875 *t = i2c_get_clientdata(client);
+ struct tda9875 *t = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
@@ -304,10 +239,9 @@ static int tda9875_get_ctrl(struct i2c_client *client,
return -EINVAL;
}
-static int tda9875_set_ctrl(struct i2c_client *client,
- struct v4l2_control *ctrl)
+static int tda9875_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct tda9875 *t = i2c_get_clientdata(client);
+ struct tda9875 *t = to_state(sd);
int chvol=0, volume, balance, left, right;
switch (ctrl->id) {
@@ -371,85 +305,105 @@ static int tda9875_set_ctrl(struct i2c_client *client,
t->rvol = -84 & 0xff;
}
-//printk("tda9875 bal:%04x vol:%04x bass:%04x treble:%04x\n",va->balance,va->volume,va->bass,va->treble);
-
- tda9875_set(client);
-
+ tda9875_set(sd);
return 0;
}
-
-static int tda9875_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tda9875_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
- dprintk("In tda9875_command...\n");
-
- switch (cmd) {
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- default:
- return -EINVAL;
- }
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
return v4l2_ctrl_query_fill_std(qc);
}
- case VIDIOC_S_CTRL:
- return tda9875_set_ctrl(client, arg);
+ return -EINVAL;
+}
- case VIDIOC_G_CTRL:
- return tda9875_get_ctrl(client, arg);
+static int tda9875_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
- default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */
+/* ----------------------------------------------------------------------- */
- /* nothing */
- dprintk("Default\n");
+static const struct v4l2_subdev_core_ops tda9875_core_ops = {
+ .queryctrl = tda9875_queryctrl,
+ .g_ctrl = tda9875_g_ctrl,
+ .s_ctrl = tda9875_s_ctrl,
+};
- } /* end of (cmd) switch */
+static const struct v4l2_subdev_ops tda9875_ops = {
+ .core = &tda9875_core_ops,
+};
- return 0;
-}
+/* ----------------------------------------------------------------------- */
-static struct i2c_driver driver = {
- .driver = {
- .name = "tda9875",
- },
- .id = I2C_DRIVERID_TDA9875,
- .attach_adapter = tda9875_probe,
- .detach_client = tda9875_detach,
- .command = tda9875_command,
-};
+/* *********************** *
+ * i2c interface functions *
+ * *********************** */
-static struct i2c_client client_template =
+static int tda9875_checkit(struct i2c_client *client, int addr)
{
- .name = "tda9875",
- .driver = &driver,
-};
+ int dic, rev;
-static int __init tda9875_init(void)
-{
- return i2c_add_driver(&driver);
+ dic = i2c_read_register(client, addr, 254);
+ rev = i2c_read_register(client, addr, 255);
+
+ if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */
+ v4l_info(client, "tda9875%s rev. %d detected at 0x%02x\n",
+ dic == 0 ? "" : "A", rev, addr << 1);
+ return 1;
+ }
+ v4l_info(client, "no such chip at 0x%02x (dic=0x%x rev=0x%x)\n",
+ addr << 1, dic, rev);
+ return 0;
}
-static void __exit tda9875_fini(void)
+static int tda9875_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- i2c_del_driver(&driver);
+ struct tda9875 *t;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ if (!tda9875_checkit(client, client->addr))
+ return -ENODEV;
+
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+ sd = &t->sd;
+ v4l2_i2c_subdev_init(sd, client, &tda9875_ops);
+
+ do_tda9875_init(sd);
+ return 0;
}
-module_init(tda9875_init);
-module_exit(tda9875_fini);
+static int tda9875_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+ do_tda9875_init(sd);
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+static const struct i2c_device_id tda9875_id[] = {
+ { "tda9875", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tda9875_id);
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tda9875",
+ .driverid = I2C_DRIVERID_TDA9875,
+ .command = tda9875_command,
+ .probe = tda9875_probe,
+ .remove = tda9875_remove,
+ .id_table = tda9875_id,
+};
diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c
index cde092adbb5a..31dde86f2df4 100644
--- a/drivers/media/video/tea6415c.c
+++ b/drivers/media/video/tea6415c.c
@@ -31,7 +31,7 @@
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "tea6415c.h"
@@ -122,31 +122,57 @@ static int switch_matrix(struct i2c_client *client, int i, int o)
return ret;
}
-static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int tea6415c_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg)
{
- struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg;
- int result = 0;
+ if (cmd == TEA6415C_SWITCH) {
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg;
- switch (cmd) {
- case TEA6415C_SWITCH:
- result = switch_matrix(client, v->in, v->out);
- break;
- default:
- return -ENOIOCTLCMD;
+ return switch_matrix(client, v->in, v->out);
}
- return result;
+ return -ENOIOCTLCMD;
}
+static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tea6415c_core_ops = {
+ .ioctl = tea6415c_ioctl,
+};
+
+static const struct v4l2_subdev_ops tea6415c_ops = {
+ .core = &tea6415c_core_ops,
+};
+
/* this function is called by i2c_probe */
static int tea6415c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
+
/* let's see whether this adapter can support what we need */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE))
return 0;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
+ sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tea6415c_ops);
+ return 0;
+}
+
+static int tea6415c_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
return 0;
}
@@ -168,6 +194,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.driverid = I2C_DRIVERID_TEA6415C,
.command = tea6415c_command,
.probe = tea6415c_probe,
+ .remove = tea6415c_remove,
.legacy_probe = tea6415c_legacy_probe,
.id_table = tea6415c_id,
};
diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c
index e50820969e64..38e519f04bde 100644
--- a/drivers/media/video/tea6420.c
+++ b/drivers/media/video/tea6420.c
@@ -31,7 +31,7 @@
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "tea6420.h"
@@ -90,26 +90,37 @@ static int tea6420_switch(struct i2c_client *client, int i, int o, int g)
return 0;
}
-static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int tea6420_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg)
{
- struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg;
- int result = 0;
+ if (cmd == TEA6420_SWITCH) {
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg;
- switch (cmd) {
- case TEA6420_SWITCH:
- result = tea6420_switch(client, a->in, a->out, a->gain);
- break;
- default:
- return -ENOIOCTLCMD;
+ return tea6420_switch(client, a->in, a->out, a->gain);
}
+ return -ENOIOCTLCMD;
+}
- return result;
+static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
}
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tea6420_core_ops = {
+ .ioctl = tea6420_ioctl,
+};
+
+static const struct v4l2_subdev_ops tea6420_ops = {
+ .core = &tea6420_core_ops,
+};
+
/* this function is called by i2c_probe */
static int tea6420_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
int err, i;
/* let's see whether this adapter can support what we need */
@@ -126,9 +137,22 @@ static int tea6420_probe(struct i2c_client *client,
}
if (err) {
v4l_dbg(1, debug, client, "could not initialize tea6420\n");
- kfree(client);
return -ENODEV;
}
+
+ sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tea6420_ops);
+ return 0;
+}
+
+static int tea6420_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
return 0;
}
@@ -150,6 +174,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.driverid = I2C_DRIVERID_TEA6420,
.command = tea6420_command,
.probe = tea6420_probe,
+ .remove = tea6420_remove,
.legacy_probe = tea6420_legacy_probe,
.id_table = tea6420_id,
};
diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c
index 281065b9dd2d..5c95ecd09dc2 100644
--- a/drivers/media/video/tlv320aic23b.c
+++ b/drivers/media/video/tlv320aic23b.c
@@ -30,7 +30,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h>
MODULE_DESCRIPTION("tlv320aic23b driver");
@@ -44,15 +44,22 @@ I2C_CLIENT_INSMOD;
/* ----------------------------------------------------------------------- */
struct tlv320aic23b_state {
+ struct v4l2_subdev sd;
u8 muted;
};
-static int tlv320aic23b_write(struct i2c_client *client, int reg, u16 val)
+static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd)
{
+ return container_of(sd, struct tlv320aic23b_state, sd);
+}
+
+static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
if ((reg < 0 || reg > 9) && (reg != 15)) {
- v4l_err(client, "Invalid register R%d\n", reg);
+ v4l2_err(sd, "Invalid register R%d\n", reg);
return -1;
}
@@ -60,61 +67,82 @@ static int tlv320aic23b_write(struct i2c_client *client, int reg, u16 val)
if (i2c_smbus_write_byte_data(client,
(reg << 1) | (val >> 8), val & 0xff) == 0)
return 0;
- v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
+ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
return -1;
}
-static int tlv320aic23b_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
- struct tlv320aic23b_state *state = i2c_get_clientdata(client);
- struct v4l2_control *ctrl = arg;
- u32 *freq = arg;
-
- switch (cmd) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- switch (*freq) {
- case 32000: /* set sample rate to 32 kHz */
- tlv320aic23b_write(client, 8, 0x018);
- break;
- case 44100: /* set sample rate to 44.1 kHz */
- tlv320aic23b_write(client, 8, 0x022);
- break;
- case 48000: /* set sample rate to 48 kHz */
- tlv320aic23b_write(client, 8, 0x000);
- break;
- default:
- return -EINVAL;
- }
- break;
-
- case VIDIOC_G_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- ctrl->value = state->muted;
+ switch (freq) {
+ case 32000: /* set sample rate to 32 kHz */
+ tlv320aic23b_write(sd, 8, 0x018);
break;
-
- case VIDIOC_S_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- state->muted = ctrl->value;
- tlv320aic23b_write(client, 0, 0x180); /* mute both channels */
- /* set gain on both channels to +3.0 dB */
- if (!state->muted)
- tlv320aic23b_write(client, 0, 0x119);
+ case 44100: /* set sample rate to 44.1 kHz */
+ tlv320aic23b_write(sd, 8, 0x022);
break;
-
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Input: %s\n",
- state->muted ? "muted" : "active");
+ case 48000: /* set sample rate to 48 kHz */
+ tlv320aic23b_write(sd, 8, 0x000);
break;
-
default:
return -EINVAL;
}
return 0;
}
+static int tlv320aic23b_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct tlv320aic23b_state *state = to_state(sd);
+
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ ctrl->value = state->muted;
+ return 0;
+}
+
+static int tlv320aic23b_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct tlv320aic23b_state *state = to_state(sd);
+
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ state->muted = ctrl->value;
+ tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */
+ /* set gain on both channels to +3.0 dB */
+ if (!state->muted)
+ tlv320aic23b_write(sd, 0, 0x119);
+ return 0;
+}
+
+static int tlv320aic23b_log_status(struct v4l2_subdev *sd)
+{
+ struct tlv320aic23b_state *state = to_state(sd);
+
+ v4l2_info(sd, "Input: %s\n", state->muted ? "muted" : "active");
+ return 0;
+}
+
+static int tlv320aic23b_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = {
+ .log_status = tlv320aic23b_log_status,
+ .g_ctrl = tlv320aic23b_g_ctrl,
+ .s_ctrl = tlv320aic23b_s_ctrl,
+};
+
+static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = {
+ .s_clock_freq = tlv320aic23b_s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops tlv320aic23b_ops = {
+ .core = &tlv320aic23b_core_ops,
+ .audio = &tlv320aic23b_audio_ops,
+};
+
/* ----------------------------------------------------------------------- */
/* i2c implementation */
@@ -128,6 +156,7 @@ static int tlv320aic23b_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tlv320aic23b_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -136,32 +165,36 @@ static int tlv320aic23b_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops);
state->muted = 0;
- i2c_set_clientdata(client, state);
/* Initialize tlv320aic23b */
/* RESET */
- tlv320aic23b_write(client, 15, 0x000);
+ tlv320aic23b_write(sd, 15, 0x000);
/* turn off DAC & mic input */
- tlv320aic23b_write(client, 6, 0x00A);
+ tlv320aic23b_write(sd, 6, 0x00A);
/* left-justified, 24-bit, master mode */
- tlv320aic23b_write(client, 7, 0x049);
+ tlv320aic23b_write(sd, 7, 0x049);
/* set gain on both channels to +3.0 dB */
- tlv320aic23b_write(client, 0, 0x119);
+ tlv320aic23b_write(sd, 0, 0x119);
/* set sample rate to 48 kHz */
- tlv320aic23b_write(client, 8, 0x000);
+ tlv320aic23b_write(sd, 8, 0x000);
/* activate digital interface */
- tlv320aic23b_write(client, 9, 0x001);
+ tlv320aic23b_write(sd, 9, 0x001);
return 0;
}
static int tlv320aic23b_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 4a7735c6c1a6..97d7509d212f 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -18,7 +18,7 @@
#include <linux/videodev.h>
#include <media/tuner.h>
#include <media/tuner-types.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "mt20xx.h"
@@ -78,6 +78,7 @@ struct tuner {
/* device */
struct dvb_frontend fe;
struct i2c_client *i2c;
+ struct v4l2_subdev sd;
struct list_head list;
unsigned int using_v4l2:1;
@@ -95,6 +96,11 @@ struct tuner {
const char *name;
};
+static inline struct tuner *to_tuner(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tuner, sd);
+}
+
/* standard i2c insmod options */
static unsigned short normal_i2c[] = {
#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE))
@@ -213,7 +219,7 @@ static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg)
static void tuner_status(struct dvb_frontend *fe);
-static struct analog_demod_ops tuner_core_ops = {
+static struct analog_demod_ops tuner_analog_ops = {
.set_params = fe_set_params,
.standby = fe_standby,
.has_signal = fe_has_signal,
@@ -224,7 +230,7 @@ static struct analog_demod_ops tuner_core_ops = {
/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
static void set_tv_freq(struct i2c_client *c, unsigned int freq)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
struct analog_parameters params = {
@@ -259,7 +265,7 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq)
static void set_radio_freq(struct i2c_client *c, unsigned int freq)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
struct analog_parameters params = {
@@ -294,7 +300,7 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq)
static void set_freq(struct i2c_client *c, unsigned long freq)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
switch (t->mode) {
case V4L2_TUNER_RADIO:
@@ -347,7 +353,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
unsigned int new_mode_mask, unsigned int new_config,
int (*tuner_callback) (void *dev, int component, int cmd, int arg))
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
unsigned char buffer[4];
@@ -470,7 +476,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
t->name = fe_tuner_ops->info.name;
t->fe.analog_demod_priv = t;
- memcpy(analog_ops, &tuner_core_ops,
+ memcpy(analog_ops, &tuner_analog_ops,
sizeof(struct analog_demod_ops));
} else {
@@ -515,7 +521,7 @@ attach_failed:
static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
(t->mode_mask & tun_setup->mode_mask))) ||
@@ -727,6 +733,8 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode,
t->mode = mode;
if (check_mode(t, cmd) == -EINVAL) {
+ tuner_dbg("Tuner doesn't support this mode. "
+ "Putting tuner to sleep\n");
t->mode = T_STANDBY;
if (analog_ops->standby)
analog_ops->standby(&t->fe);
@@ -748,43 +756,58 @@ static inline int check_v4l2(struct tuner *t)
return 0;
}
-static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type)
{
- struct tuner *t = i2c_get_clientdata(client);
- struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
+ type->type,
+ type->addr,
+ type->mode_mask,
+ type->config);
+
+ set_addr(client, type);
+ return 0;
+}
+
+static int tuner_s_radio(struct v4l2_subdev *sd)
+{
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
+ == -EINVAL)
+ return 0;
+ if (t->radio_freq)
+ set_freq(client, t->radio_freq);
+ return 0;
+}
+
+static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby)
+{
+ struct tuner *t = to_tuner(sd);
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
- if (tuner_debug > 1) {
- v4l_i2c_print_ioctl(client,cmd);
- printk("\n");
- }
+ tuner_dbg("Putting tuner to sleep\n");
+
+ if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL)
+ return 0;
+ t->mode = T_STANDBY;
+ if (analog_ops->standby)
+ analog_ops->standby(&t->fe);
+ return 0;
+}
- switch (cmd) {
- /* --- configuration --- */
- case TUNER_SET_TYPE_ADDR:
- tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
- ((struct tuner_setup *)arg)->type,
- ((struct tuner_setup *)arg)->addr,
- ((struct tuner_setup *)arg)->mode_mask,
- ((struct tuner_setup *)arg)->config);
-
- set_addr(client, (struct tuner_setup *)arg);
- break;
- case AUDC_SET_RADIO:
- if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
- == -EINVAL)
- return 0;
- if (t->radio_freq)
- set_freq(client, t->radio_freq);
- break;
- case TUNER_SET_STANDBY:
- if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL)
- return 0;
- t->mode = T_STANDBY;
- if (analog_ops->standby)
- analog_ops->standby(&t->fe);
- break;
#ifdef CONFIG_VIDEO_ALLOW_V4L1
+static int tuner_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+ struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+
+ switch (cmd) {
case VIDIOCSAUDIO:
if (check_mode(t, "VIDIOCSAUDIO") == -EINVAL)
return 0;
@@ -897,149 +920,172 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
}
return 0;
}
+ }
+ return -ENOIOCTLCMD;
+}
#endif
- case TUNER_SET_CONFIG:
- {
- struct v4l2_priv_tun_config *cfg = arg;
- if (t->type != cfg->tuner)
- break;
+static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg)
+{
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
- if (analog_ops->set_config) {
- analog_ops->set_config(&t->fe, cfg->priv);
- break;
- }
+ if (t->type != cfg->tuner)
+ return 0;
- tuner_dbg("Tuner frontend module has no way to set config\n");
- break;
+ if (analog_ops->set_config) {
+ analog_ops->set_config(&t->fe, cfg->priv);
+ return 0;
}
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg;
- if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
- == -EINVAL)
- return 0;
+ tuner_dbg("Tuner frontend module has no way to set config\n");
+ return 0;
+}
- switch_v4l2();
+/* --- v4l ioctls --- */
+/* take care: bttv does userspace copying, we'll get a
+ kernel pointer here... */
+static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- t->std = *id;
- tuner_fixup_std(t);
- if (t->tv_freq)
- set_freq(client, t->tv_freq);
- break;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
+ == -EINVAL)
+ return 0;
- if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
- == -EINVAL)
- return 0;
- switch_v4l2();
- set_freq(client,f->frequency);
+ switch_v4l2();
- break;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ t->std = std;
+ tuner_fixup_std(t);
+ if (t->tv_freq)
+ set_freq(client, t->tv_freq);
+ return 0;
+}
- if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL)
- return 0;
- switch_v4l2();
- f->type = t->mode;
- if (fe_tuner_ops->get_frequency) {
- u32 abs_freq;
-
- fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
- f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
- (abs_freq * 2 + 125/2) / 125 :
- (abs_freq + 62500/2) / 62500;
- break;
- }
- f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
- t->radio_freq : t->tv_freq;
- break;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *tuner = arg;
+static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL)
- return 0;
- switch_v4l2();
-
- tuner->type = t->mode;
- if (analog_ops->get_afc)
- tuner->afc = analog_ops->get_afc(&t->fe);
- if (t->mode == V4L2_TUNER_ANALOG_TV)
- tuner->capability |= V4L2_TUNER_CAP_NORM;
- if (t->mode != V4L2_TUNER_RADIO) {
- tuner->rangelow = tv_range[0] * 16;
- tuner->rangehigh = tv_range[1] * 16;
- break;
- }
+ if (set_mode(client, t, f->type, "VIDIOC_S_FREQUENCY")
+ == -EINVAL)
+ return 0;
+ switch_v4l2();
+ set_freq(client, f->frequency);
- /* radio mode */
- tuner->rxsubchans =
- V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- if (fe_tuner_ops->get_status) {
- u32 tuner_status;
-
- fe_tuner_ops->get_status(&t->fe, &tuner_status);
- tuner->rxsubchans =
- (tuner_status & TUNER_STATUS_STEREO) ?
- V4L2_TUNER_SUB_STEREO :
- V4L2_TUNER_SUB_MONO;
- } else {
- if (analog_ops->is_stereo) {
- tuner->rxsubchans =
- analog_ops->is_stereo(&t->fe) ?
- V4L2_TUNER_SUB_STEREO :
- V4L2_TUNER_SUB_MONO;
- }
- }
- if (analog_ops->has_signal)
- tuner->signal = analog_ops->has_signal(&t->fe);
- tuner->capability |=
- V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
- tuner->audmode = t->audmode;
- tuner->rangelow = radio_range[0] * 16000;
- tuner->rangehigh = radio_range[1] * 16000;
- break;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *tuner = arg;
+ return 0;
+}
- if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL)
- return 0;
+static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+ struct tuner *t = to_tuner(sd);
+ struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
- switch_v4l2();
+ if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL)
+ return 0;
+ switch_v4l2();
+ f->type = t->mode;
+ if (fe_tuner_ops->get_frequency) {
+ u32 abs_freq;
+
+ fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
+ f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
+ (abs_freq * 2 + 125/2) / 125 :
+ (abs_freq + 62500/2) / 62500;
+ return 0;
+ }
+ f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
+ t->radio_freq : t->tv_freq;
+ return 0;
+}
- /* do nothing unless we're a radio tuner */
- if (t->mode != V4L2_TUNER_RADIO)
- break;
- t->audmode = tuner->audmode;
- set_radio_freq(client, t->radio_freq);
- break;
+static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+ struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+
+ if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL)
+ return 0;
+ switch_v4l2();
+
+ vt->type = t->mode;
+ if (analog_ops->get_afc)
+ vt->afc = analog_ops->get_afc(&t->fe);
+ if (t->mode == V4L2_TUNER_ANALOG_TV)
+ vt->capability |= V4L2_TUNER_CAP_NORM;
+ if (t->mode != V4L2_TUNER_RADIO) {
+ vt->rangelow = tv_range[0] * 16;
+ vt->rangehigh = tv_range[1] * 16;
+ return 0;
+ }
+
+ /* radio mode */
+ vt->rxsubchans =
+ V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ if (fe_tuner_ops->get_status) {
+ u32 tuner_status;
+
+ fe_tuner_ops->get_status(&t->fe, &tuner_status);
+ vt->rxsubchans =
+ (tuner_status & TUNER_STATUS_STEREO) ?
+ V4L2_TUNER_SUB_STEREO :
+ V4L2_TUNER_SUB_MONO;
+ } else {
+ if (analog_ops->is_stereo) {
+ vt->rxsubchans =
+ analog_ops->is_stereo(&t->fe) ?
+ V4L2_TUNER_SUB_STEREO :
+ V4L2_TUNER_SUB_MONO;
}
- case VIDIOC_LOG_STATUS:
- if (analog_ops->tuner_status)
- analog_ops->tuner_status(&t->fe);
- break;
}
+ if (analog_ops->has_signal)
+ vt->signal = analog_ops->has_signal(&t->fe);
+ vt->capability |=
+ V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+ vt->audmode = t->audmode;
+ vt->rangelow = radio_range[0] * 16000;
+ vt->rangehigh = radio_range[1] * 16000;
+ return 0;
+}
+
+static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL)
+ return 0;
+
+ switch_v4l2();
+
+ /* do nothing unless we're a radio tuner */
+ if (t->mode != V4L2_TUNER_RADIO)
+ return 0;
+ t->audmode = vt->audmode;
+ set_radio_freq(client, t->radio_freq);
return 0;
}
+static int tuner_log_status(struct v4l2_subdev *sd)
+{
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ if (analog_ops->tuner_status)
+ analog_ops->tuner_status(&t->fe);
+ return 0;
+}
+
+static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
static int tuner_suspend(struct i2c_client *c, pm_message_t state)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
tuner_dbg("suspend\n");
/* FIXME: power down ??? */
@@ -1048,7 +1094,7 @@ static int tuner_suspend(struct i2c_client *c, pm_message_t state)
static int tuner_resume(struct i2c_client *c)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
tuner_dbg("resume\n");
if (V4L2_TUNER_RADIO == t->mode) {
@@ -1061,6 +1107,32 @@ static int tuner_resume(struct i2c_client *c)
return 0;
}
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tuner_core_ops = {
+ .log_status = tuner_log_status,
+ .s_standby = tuner_s_standby,
+#ifdef CONFIG_VIDEO_ALLOW_V4L1
+ .ioctl = tuner_ioctl,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
+ .s_std = tuner_s_std,
+ .s_radio = tuner_s_radio,
+ .g_tuner = tuner_g_tuner,
+ .s_tuner = tuner_s_tuner,
+ .s_frequency = tuner_s_frequency,
+ .g_frequency = tuner_g_frequency,
+ .s_type_addr = tuner_s_type_addr,
+ .s_config = tuner_s_config,
+};
+
+static const struct v4l2_subdev_ops tuner_ops = {
+ .core = &tuner_core_ops,
+ .tuner = &tuner_tuner_ops,
+};
+
/* ---------------------------------------------------------------------- */
static LIST_HEAD(tuner_list);
@@ -1109,9 +1181,9 @@ static int tuner_probe(struct i2c_client *client,
t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
if (NULL == t)
return -ENOMEM;
+ v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops);
t->i2c = client;
t->name = "(tuner unset)";
- i2c_set_clientdata(client, t);
t->type = UNSET;
t->audmode = V4L2_TUNER_MODE_STEREO;
t->mode_mask = T_UNINITIALIZED;
@@ -1261,8 +1333,9 @@ static int tuner_legacy_probe(struct i2c_adapter *adap)
static int tuner_remove(struct i2c_client *client)
{
- struct tuner *t = i2c_get_clientdata(client);
+ struct tuner *t = to_tuner(i2c_get_clientdata(client));
+ v4l2_device_unregister_subdev(&t->sd);
tuner_detach(&t->fe);
t->fe.analog_demod_priv = NULL;
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index 3720f0e03a16..d0c794da735b 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -33,8 +33,7 @@
#include <linux/freezer.h>
#include <media/tvaudio.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv-legacy.h>
@@ -110,7 +109,7 @@ struct CHIPDESC {
/* current state of the chip */
struct CHIPSTATE {
- struct i2c_client *c;
+ struct v4l2_subdev sd;
/* chip-specific description - should point to
an entry at CHIPDESC table */
@@ -132,6 +131,11 @@ struct CHIPSTATE {
int audmode;
};
+static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct CHIPSTATE, sd);
+}
+
/* ---------------------------------------------------------------------- */
/* i2c addresses */
@@ -152,34 +156,34 @@ I2C_CLIENT_INSMOD;
static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
if (subaddr < 0) {
- v4l_dbg(1, debug, chip->c, "%s: chip_write: 0x%x\n",
- chip->c->name, val);
+ v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val);
chip->shadow.bytes[1] = val;
buffer[0] = val;
- if (1 != i2c_master_send(chip->c,buffer,1)) {
- v4l_warn(chip->c, "%s: I/O error (write 0x%x)\n",
- chip->c->name, val);
+ if (1 != i2c_master_send(c, buffer, 1)) {
+ v4l2_warn(sd, "I/O error (write 0x%x)\n", val);
return -1;
}
} else {
if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
- v4l_info(chip->c,
+ v4l2_info(sd,
"Tried to access a non-existent register: %d\n",
subaddr);
return -EINVAL;
}
- v4l_dbg(1, debug, chip->c, "%s: chip_write: reg%d=0x%x\n",
- chip->c->name, subaddr, val);
+ v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n",
+ subaddr, val);
chip->shadow.bytes[subaddr+1] = val;
buffer[0] = subaddr;
buffer[1] = val;
- if (2 != i2c_master_send(chip->c,buffer,2)) {
- v4l_warn(chip->c, "%s: I/O error (write reg%d=0x%x)\n",
- chip->c->name, subaddr, val);
+ if (2 != i2c_master_send(c, buffer, 2)) {
+ v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n",
+ subaddr, val);
return -1;
}
}
@@ -189,12 +193,14 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
static int chip_write_masked(struct CHIPSTATE *chip,
int subaddr, int val, int mask)
{
+ struct v4l2_subdev *sd = &chip->sd;
+
if (mask != 0) {
if (subaddr < 0) {
val = (chip->shadow.bytes[1] & ~mask) | (val & mask);
} else {
if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
- v4l_info(chip->c,
+ v4l2_info(sd,
"Tried to access a non-existent register: %d\n",
subaddr);
return -EINVAL;
@@ -208,45 +214,51 @@ static int chip_write_masked(struct CHIPSTATE *chip,
static int chip_read(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer;
- if (1 != i2c_master_recv(chip->c,&buffer,1)) {
- v4l_warn(chip->c, "%s: I/O error (read)\n",
- chip->c->name);
+ if (1 != i2c_master_recv(c, &buffer, 1)) {
+ v4l2_warn(sd, "I/O error (read)\n");
return -1;
}
- v4l_dbg(1, debug, chip->c, "%s: chip_read: 0x%x\n",chip->c->name, buffer);
+ v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer);
return buffer;
}
static int chip_read2(struct CHIPSTATE *chip, int subaddr)
{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char write[1];
unsigned char read[1];
struct i2c_msg msgs[2] = {
- { chip->c->addr, 0, 1, write },
- { chip->c->addr, I2C_M_RD, 1, read }
+ { c->addr, 0, 1, write },
+ { c->addr, I2C_M_RD, 1, read }
};
+
write[0] = subaddr;
- if (2 != i2c_transfer(chip->c->adapter,msgs,2)) {
- v4l_warn(chip->c, "%s: I/O error (read2)\n", chip->c->name);
+ if (2 != i2c_transfer(c->adapter, msgs, 2)) {
+ v4l2_warn(sd, "I/O error (read2)\n");
return -1;
}
- v4l_dbg(1, debug, chip->c, "%s: chip_read2: reg%d=0x%x\n",
- chip->c->name, subaddr,read[0]);
+ v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n",
+ subaddr, read[0]);
return read[0];
}
static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
int i;
if (0 == cmd->count)
return 0;
if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
- v4l_info(chip->c,
+ v4l2_info(sd,
"Tried to access a non-existent register range: %d to %d\n",
cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1);
return -EINVAL;
@@ -255,19 +267,19 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
/* FIXME: it seems that the shadow bytes are wrong bellow !*/
/* update our shadow register set; print bytes if (debug > 0) */
- v4l_dbg(1, debug, chip->c, "%s: chip_cmd(%s): reg=%d, data:",
- chip->c->name, name,cmd->bytes[0]);
+ v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:",
+ name, cmd->bytes[0]);
for (i = 1; i < cmd->count; i++) {
if (debug)
- printk(" 0x%x",cmd->bytes[i]);
+ printk(KERN_CONT " 0x%x", cmd->bytes[i]);
chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i];
}
if (debug)
- printk("\n");
+ printk(KERN_CONT "\n");
/* send data to the chip */
- if (cmd->count != i2c_master_send(chip->c,cmd->bytes,cmd->count)) {
- v4l_warn(chip->c, "%s: I/O error (%s)\n", chip->c->name, name);
+ if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) {
+ v4l2_warn(sd, "I/O error (%s)\n", name);
return -1;
}
return 0;
@@ -290,9 +302,10 @@ static int chip_thread(void *data)
{
struct CHIPSTATE *chip = data;
struct CHIPDESC *desc = chip->desc;
+ struct v4l2_subdev *sd = &chip->sd;
int mode;
- v4l_dbg(1, debug, chip->c, "%s: thread started\n", chip->c->name);
+ v4l2_dbg(1, debug, sd, "thread started\n");
set_freezable();
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
@@ -302,7 +315,7 @@ static int chip_thread(void *data)
try_to_freeze();
if (kthread_should_stop())
break;
- v4l_dbg(1, debug, chip->c, "%s: thread wakeup\n", chip->c->name);
+ v4l2_dbg(1, debug, sd, "thread wakeup\n");
/* don't do anything for radio or if mode != auto */
if (chip->radio || chip->mode != 0)
@@ -314,8 +327,7 @@ static int chip_thread(void *data)
continue;
/* chip detected a new audio mode - set it */
- v4l_dbg(1, debug, chip->c, "%s: thread checkmode\n",
- chip->c->name);
+ v4l2_dbg(1, debug, sd, "thread checkmode\n");
chip->prevmode = mode;
@@ -334,7 +346,7 @@ static int chip_thread(void *data)
mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
}
- v4l_dbg(1, debug, chip->c, "%s: thread exiting\n", chip->c->name);
+ v4l2_dbg(1, debug, sd, "thread exiting\n");
return 0;
}
@@ -363,6 +375,7 @@ static int chip_thread(void *data)
static int tda9840_getmode(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
int val, mode;
val = chip_read(chip);
@@ -372,7 +385,7 @@ static int tda9840_getmode(struct CHIPSTATE *chip)
if (val & TDA9840_ST_STEREO)
mode |= V4L2_TUNER_MODE_STEREO;
- v4l_dbg(1, debug, chip->c, "tda9840_getmode(): raw chip read: %d, return: %d\n",
+ v4l2_dbg(1, debug, sd, "tda9840_getmode(): raw chip read: %d, return: %d\n",
val, mode);
return mode;
}
@@ -668,6 +681,7 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
static int tda9873_getmode(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
int val,mode;
val = chip_read(chip);
@@ -676,23 +690,24 @@ static int tda9873_getmode(struct CHIPSTATE *chip)
mode |= V4L2_TUNER_MODE_STEREO;
if (val & TDA9873_DUAL)
mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
- v4l_dbg(1, debug, chip->c, "tda9873_getmode(): raw chip read: %d, return: %d\n",
+ v4l2_dbg(1, debug, sd, "tda9873_getmode(): raw chip read: %d, return: %d\n",
val, mode);
return mode;
}
static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
{
+ struct v4l2_subdev *sd = &chip->sd;
int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK;
/* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */
if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) {
- v4l_dbg(1, debug, chip->c, "tda9873_setmode(): external input\n");
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): external input\n");
return;
}
- v4l_dbg(1, debug, chip->c, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
- v4l_dbg(1, debug, chip->c, "tda9873_setmode(): sw_data = %d\n", sw_data);
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): sw_data = %d\n", sw_data);
switch (mode) {
case V4L2_TUNER_MODE_MONO:
@@ -713,7 +728,7 @@ static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
}
chip_write(chip, TDA9873_SW, sw_data);
- v4l_dbg(1, debug, chip->c, "tda9873_setmode(): req. mode %d; chip_write: %d\n",
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): req. mode %d; chip_write: %d\n",
mode, sw_data);
}
@@ -822,6 +837,8 @@ static struct tda9874a_MODES {
static int tda9874a_setup(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
+
chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */
chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR);
chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02);
@@ -852,13 +869,14 @@ static int tda9874a_setup(struct CHIPSTATE *chip)
chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80);
chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */
}
- v4l_dbg(1, debug, chip->c, "tda9874a_setup(): %s [0x%02X].\n",
+ v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n",
tda9874a_modelist[tda9874a_STD].name,tda9874a_STD);
return 1;
}
static int tda9874a_getmode(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
int dsr,nsr,mode;
int necr; /* just for debugging */
@@ -895,16 +913,18 @@ static int tda9874a_getmode(struct CHIPSTATE *chip)
mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
}
- v4l_dbg(1, debug, chip->c, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
+ v4l2_dbg(1, debug, sd, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
dsr, nsr, necr, mode);
return mode;
}
static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
{
+ struct v4l2_subdev *sd = &chip->sd;
+
/* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */
/* If auto-muting is disabled, we can hear a signal of degrading quality. */
- if(tda9874a_mode) {
+ if (tda9874a_mode) {
if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */
tda9874a_NCONR &= 0xfe; /* enable */
else
@@ -941,7 +961,7 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
chip_write(chip, TDA9874A_AOSR, aosr);
chip_write(chip, TDA9874A_MDACOSR, mdacosr);
- v4l_dbg(1, debug, chip->c, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
+ v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
mode, aosr, mdacosr);
} else { /* dic == 0x07 */
@@ -976,13 +996,14 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
chip_write(chip, TDA9874A_FMMR, fmmr);
chip_write(chip, TDA9874A_AOSR, aosr);
- v4l_dbg(1, debug, chip->c, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
+ v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
mode, fmmr, aosr);
}
}
static int tda9874a_checkit(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
int dic,sic; /* device id. and software id. codes */
if(-1 == (dic = chip_read2(chip,TDA9874A_DIC)))
@@ -990,10 +1011,10 @@ static int tda9874a_checkit(struct CHIPSTATE *chip)
if(-1 == (sic = chip_read2(chip,TDA9874A_SIC)))
return 0;
- v4l_dbg(1, debug, chip->c, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic);
+ v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic);
if((dic == 0x11)||(dic == 0x07)) {
- v4l_info(chip->c, "found tda9874%s.\n", (dic == 0x11) ? "a":"h");
+ v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h");
tda9874a_dic = dic; /* remember device id. */
return 1;
}
@@ -1113,12 +1134,12 @@ static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; }
static int tda8425_initialize(struct CHIPSTATE *chip)
{
struct CHIPDESC *desc = chip->desc;
+ struct i2c_client *c = v4l2_get_subdevdata(&chip->sd);
int inputmap[4] = { /* tuner */ TDA8425_S1_CH2, /* radio */ TDA8425_S1_CH1,
/* extern */ TDA8425_S1_CH1, /* intern */ TDA8425_S1_OFF};
- if (chip->c->adapter->id == I2C_HW_B_RIVA) {
- memcpy (desc->inputmap, inputmap, sizeof (inputmap));
- }
+ if (c->adapter->id == I2C_HW_B_RIVA)
+ memcpy(desc->inputmap, inputmap, sizeof(inputmap));
return 0;
}
@@ -1215,9 +1236,11 @@ static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT
static void ta8874z_setmode(struct CHIPSTATE *chip, int mode)
{
+ struct v4l2_subdev *sd = &chip->sd;
int update = 1;
audiocmd *t = NULL;
- v4l_dbg(1, debug, chip->c, "ta8874z_setmode(): mode: 0x%02x\n", mode);
+
+ v4l2_dbg(1, debug, sd, "ta8874z_setmode(): mode: 0x%02x\n", mode);
switch(mode){
case V4L2_TUNER_MODE_MONO:
@@ -1479,142 +1502,11 @@ static struct CHIPDESC chiplist[] = {
/* ---------------------------------------------------------------------- */
-/* i2c registration */
-
-static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id)
-{
- struct CHIPSTATE *chip;
- struct CHIPDESC *desc;
-
- if (debug) {
- printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
- printk(KERN_INFO "tvaudio: known chips: ");
- for (desc = chiplist; desc->name != NULL; desc++)
- printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name);
- printk("\n");
- }
-
- chip = kzalloc(sizeof(*chip),GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
- chip->c = client;
- i2c_set_clientdata(client, chip);
- /* find description for the chip */
- v4l_dbg(1, debug, client, "chip found @ 0x%x\n", client->addr<<1);
- for (desc = chiplist; desc->name != NULL; desc++) {
- if (0 == *(desc->insmodopt))
- continue;
- if (client->addr < desc->addr_lo ||
- client->addr > desc->addr_hi)
- continue;
- if (desc->checkit && !desc->checkit(chip))
- continue;
- break;
- }
- if (desc->name == NULL) {
- v4l_dbg(1, debug, client, "no matching chip description found\n");
- kfree(chip);
- return -EIO;
- }
- v4l_info(client, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name);
- if (desc->flags) {
- v4l_dbg(1, debug, client, "matches:%s%s%s.\n",
- (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "",
- (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
- (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : "");
- }
-
- /* fill required data structures */
- if (!id)
- strlcpy(client->name, desc->name, I2C_NAME_SIZE);
- chip->desc = desc;
- chip->shadow.count = desc->registers+1;
- chip->prevmode = -1;
- chip->audmode = V4L2_TUNER_MODE_LANG1;
-
- /* initialization */
- if (desc->initialize != NULL)
- desc->initialize(chip);
- else
- chip_cmd(chip,"init",&desc->init);
-
- if (desc->flags & CHIP_HAS_VOLUME) {
- if (!desc->volfunc) {
- /* This shouldn't be happen. Warn user, but keep working
- without volume controls
- */
- v4l_info(chip->c, "volume callback undefined!\n");
- desc->flags &= ~CHIP_HAS_VOLUME;
- } else {
- chip->left = desc->leftinit ? desc->leftinit : 65535;
- chip->right = desc->rightinit ? desc->rightinit : 65535;
- chip_write(chip, desc->leftreg,
- desc->volfunc(chip->left));
- chip_write(chip, desc->rightreg,
- desc->volfunc(chip->right));
- }
- }
- if (desc->flags & CHIP_HAS_BASSTREBLE) {
- if (!desc->bassfunc || !desc->treblefunc) {
- /* This shouldn't be happen. Warn user, but keep working
- without bass/treble controls
- */
- v4l_info(chip->c, "bass/treble callbacks undefined!\n");
- desc->flags &= ~CHIP_HAS_BASSTREBLE;
- } else {
- chip->treble = desc->trebleinit ?
- desc->trebleinit : 32768;
- chip->bass = desc->bassinit ?
- desc->bassinit : 32768;
- chip_write(chip, desc->bassreg,
- desc->bassfunc(chip->bass));
- chip_write(chip, desc->treblereg,
- desc->treblefunc(chip->treble));
- }
- }
-
- chip->thread = NULL;
- if (desc->flags & CHIP_NEED_CHECKMODE) {
- if (!desc->getmode || !desc->setmode) {
- /* This shouldn't be happen. Warn user, but keep working
- without kthread
- */
- v4l_info(chip->c, "set/get mode callbacks undefined!\n");
- return 0;
- }
- /* start async thread */
- init_timer(&chip->wt);
- chip->wt.function = chip_thread_wake;
- chip->wt.data = (unsigned long)chip;
- chip->thread = kthread_run(chip_thread, chip, chip->c->name);
- if (IS_ERR(chip->thread)) {
- v4l_warn(chip->c, "%s: failed to create kthread\n",
- chip->c->name);
- chip->thread = NULL;
- }
- }
- return 0;
-}
-
-static int chip_remove(struct i2c_client *client)
-{
- struct CHIPSTATE *chip = i2c_get_clientdata(client);
-
- del_timer_sync(&chip->wt);
- if (chip->thread) {
- /* shutdown async thread */
- kthread_stop(chip->thread);
- chip->thread = NULL;
- }
-
- kfree(chip);
- return 0;
-}
-
-static int tvaudio_get_ctrl(struct CHIPSTATE *chip,
+static int tvaudio_g_ctrl(struct v4l2_subdev *sd,
struct v4l2_control *ctrl)
{
+ struct CHIPSTATE *chip = to_state(sd);
struct CHIPDESC *desc = chip->desc;
switch (ctrl->id) {
@@ -1652,9 +1544,10 @@ static int tvaudio_get_ctrl(struct CHIPSTATE *chip,
return -EINVAL;
}
-static int tvaudio_set_ctrl(struct CHIPSTATE *chip,
+static int tvaudio_s_ctrl(struct v4l2_subdev *sd,
struct v4l2_control *ctrl)
{
+ struct CHIPSTATE *chip = to_state(sd);
struct CHIPDESC *desc = chip->desc;
switch (ctrl->id) {
@@ -1726,161 +1619,327 @@ static int tvaudio_set_ctrl(struct CHIPSTATE *chip,
/* ---------------------------------------------------------------------- */
/* video4linux interface */
-static int chip_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tvaudio_s_radio(struct v4l2_subdev *sd)
{
- struct CHIPSTATE *chip = i2c_get_clientdata(client);
- struct CHIPDESC *desc = chip->desc;
+ struct CHIPSTATE *chip = to_state(sd);
- if (debug > 0) {
- v4l_i2c_print_ioctl(chip->c, cmd);
- printk("\n");
+ chip->radio = 1;
+ chip->watch_stereo = 0;
+ /* del_timer(&chip->wt); */
+ return 0;
+}
+
+static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ if (!(desc->flags & CHIP_HAS_VOLUME))
+ return -EINVAL;
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ if (!(desc->flags & CHIP_HAS_BASSTREBLE))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
}
+ return v4l2_ctrl_query_fill_std(qc);
+}
+
+static int tvaudio_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
- switch (cmd) {
- case AUDC_SET_RADIO:
- chip->radio = 1;
+ if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4)
+ return -EINVAL;
+ /* There are four inputs: tuner, radio, extern and intern. */
+ chip->input = rt->input;
+ if (chip->muted)
+ return 0;
+ chip_write_masked(chip, desc->inputreg,
+ desc->inputmap[chip->input], desc->inputmask);
+ return 0;
+}
+
+static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+ int mode = 0;
+
+ if (chip->radio)
+ return 0;
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ case V4L2_TUNER_MODE_LANG2:
+ mode = vt->audmode;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ mode = V4L2_TUNER_MODE_STEREO;
+ break;
+ default:
+ return -EINVAL;
+ }
+ chip->audmode = vt->audmode;
+
+ if (desc->setmode && mode) {
chip->watch_stereo = 0;
/* del_timer(&chip->wt); */
- break;
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BALANCE:
- if (!(desc->flags & CHIP_HAS_VOLUME))
- return -EINVAL;
- break;
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- if (!(desc->flags & CHIP_HAS_BASSTREBLE))
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
- return v4l2_ctrl_query_fill_std(qc);
+ chip->mode = mode;
+ desc->setmode(chip, mode);
}
- case VIDIOC_S_CTRL:
- return tvaudio_set_ctrl(chip, arg);
+ return 0;
+}
- case VIDIOC_G_CTRL:
- return tvaudio_get_ctrl(chip, arg);
- case VIDIOC_INT_G_AUDIO_ROUTING:
- {
- struct v4l2_routing *rt = arg;
+static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+ int mode = V4L2_TUNER_MODE_MONO;
- rt->input = chip->input;
- rt->output = 0;
- break;
+ if (chip->radio)
+ return 0;
+ vt->audmode = chip->audmode;
+ vt->rxsubchans = 0;
+ vt->capability = V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+
+ if (desc->getmode)
+ mode = desc->getmode(chip);
+
+ if (mode & V4L2_TUNER_MODE_MONO)
+ vt->rxsubchans |= V4L2_TUNER_SUB_MONO;
+ if (mode & V4L2_TUNER_MODE_STEREO)
+ vt->rxsubchans |= V4L2_TUNER_SUB_STEREO;
+ /* Note: for SAP it should be mono/lang2 or stereo/lang2.
+ When this module is converted fully to v4l2, then this
+ should change for those chips that can detect SAP. */
+ if (mode & V4L2_TUNER_MODE_LANG1)
+ vt->rxsubchans = V4L2_TUNER_SUB_LANG1 |
+ V4L2_TUNER_SUB_LANG2;
+ return 0;
+}
+
+static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+
+ chip->radio = 0;
+ return 0;
+}
+
+static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ chip->mode = 0; /* automatic */
+
+ /* For chips that provide getmode and setmode, and doesn't
+ automatically follows the stereo carrier, a kthread is
+ created to set the audio standard. In this case, when then
+ the video channel is changed, tvaudio starts on MONO mode.
+ After waiting for 2 seconds, the kernel thread is called,
+ to follow whatever audio standard is pointed by the
+ audio carrier.
+ */
+ if (chip->thread) {
+ desc->setmode(chip, V4L2_TUNER_MODE_MONO);
+ if (chip->prevmode != V4L2_TUNER_MODE_MONO)
+ chip->prevmode = -1; /* reset previous mode */
+ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
}
- case VIDIOC_INT_S_AUDIO_ROUTING:
- {
- struct v4l2_routing *rt = arg;
+ return 0;
+}
- if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4)
- return -EINVAL;
- /* There are four inputs: tuner, radio, extern and intern. */
- chip->input = rt->input;
- if (chip->muted)
- break;
- chip_write_masked(chip, desc->inputreg,
- desc->inputmap[chip->input], desc->inputmask);
- break;
+static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0);
+}
+
+static int tvaudio_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tvaudio_core_ops = {
+ .g_chip_ident = tvaudio_g_chip_ident,
+ .queryctrl = tvaudio_queryctrl,
+ .g_ctrl = tvaudio_g_ctrl,
+ .s_ctrl = tvaudio_s_ctrl,
+};
+
+static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = {
+ .s_radio = tvaudio_s_radio,
+ .s_frequency = tvaudio_s_frequency,
+ .s_std = tvaudio_s_std,
+ .s_tuner = tvaudio_s_tuner,
+ .s_tuner = tvaudio_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = {
+ .s_routing = tvaudio_s_routing,
+};
+
+static const struct v4l2_subdev_ops tvaudio_ops = {
+ .core = &tvaudio_core_ops,
+ .tuner = &tvaudio_tuner_ops,
+ .audio = &tvaudio_audio_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+
+/* i2c registration */
+
+static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct CHIPSTATE *chip;
+ struct CHIPDESC *desc;
+ struct v4l2_subdev *sd;
+
+ if (debug) {
+ printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
+ printk(KERN_INFO "tvaudio: known chips: ");
+ for (desc = chiplist; desc->name != NULL; desc++)
+ printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name);
+ printk("\n");
}
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *vt = arg;
- int mode = 0;
- if (chip->radio)
- break;
- switch (vt->audmode) {
- case V4L2_TUNER_MODE_MONO:
- case V4L2_TUNER_MODE_STEREO:
- case V4L2_TUNER_MODE_LANG1:
- case V4L2_TUNER_MODE_LANG2:
- mode = vt->audmode;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- mode = V4L2_TUNER_MODE_STEREO;
- break;
- default:
- return -EINVAL;
- }
- chip->audmode = vt->audmode;
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ sd = &chip->sd;
+ v4l2_i2c_subdev_init(sd, client, &tvaudio_ops);
- if (desc->setmode && mode) {
- chip->watch_stereo = 0;
- /* del_timer(&chip->wt); */
- chip->mode = mode;
- desc->setmode(chip, mode);
- }
+ /* find description for the chip */
+ v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1);
+ for (desc = chiplist; desc->name != NULL; desc++) {
+ if (0 == *(desc->insmodopt))
+ continue;
+ if (client->addr < desc->addr_lo ||
+ client->addr > desc->addr_hi)
+ continue;
+ if (desc->checkit && !desc->checkit(chip))
+ continue;
break;
}
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = arg;
- int mode = V4L2_TUNER_MODE_MONO;
+ if (desc->name == NULL) {
+ v4l2_dbg(1, debug, sd, "no matching chip description found\n");
+ kfree(chip);
+ return -EIO;
+ }
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name);
+ if (desc->flags) {
+ v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n",
+ (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "",
+ (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
+ (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : "");
+ }
- if (chip->radio)
- break;
- vt->audmode = chip->audmode;
- vt->rxsubchans = 0;
- vt->capability = V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ /* fill required data structures */
+ if (!id)
+ strlcpy(client->name, desc->name, I2C_NAME_SIZE);
+ chip->desc = desc;
+ chip->shadow.count = desc->registers+1;
+ chip->prevmode = -1;
+ chip->audmode = V4L2_TUNER_MODE_LANG1;
- if (desc->getmode)
- mode = desc->getmode(chip);
+ /* initialization */
+ if (desc->initialize != NULL)
+ desc->initialize(chip);
+ else
+ chip_cmd(chip, "init", &desc->init);
- if (mode & V4L2_TUNER_MODE_MONO)
- vt->rxsubchans |= V4L2_TUNER_SUB_MONO;
- if (mode & V4L2_TUNER_MODE_STEREO)
- vt->rxsubchans |= V4L2_TUNER_SUB_STEREO;
- /* Note: for SAP it should be mono/lang2 or stereo/lang2.
- When this module is converted fully to v4l2, then this
- should change for those chips that can detect SAP. */
- if (mode & V4L2_TUNER_MODE_LANG1)
- vt->rxsubchans = V4L2_TUNER_SUB_LANG1 |
- V4L2_TUNER_SUB_LANG2;
- break;
+ if (desc->flags & CHIP_HAS_VOLUME) {
+ if (!desc->volfunc) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without volume controls
+ */
+ v4l2_info(sd, "volume callback undefined!\n");
+ desc->flags &= ~CHIP_HAS_VOLUME;
+ } else {
+ chip->left = desc->leftinit ? desc->leftinit : 65535;
+ chip->right = desc->rightinit ? desc->rightinit : 65535;
+ chip_write(chip, desc->leftreg,
+ desc->volfunc(chip->left));
+ chip_write(chip, desc->rightreg,
+ desc->volfunc(chip->right));
+ }
}
- case VIDIOC_S_STD:
- chip->radio = 0;
- break;
- case VIDIOC_S_FREQUENCY:
- chip->mode = 0; /* automatic */
-
- /* For chips that provide getmode and setmode, and doesn't
- automatically follows the stereo carrier, a kthread is
- created to set the audio standard. In this case, when then
- the video channel is changed, tvaudio starts on MONO mode.
- After waiting for 2 seconds, the kernel thread is called,
- to follow whatever audio standard is pointed by the
- audio carrier.
- */
- if (chip->thread) {
- desc->setmode(chip,V4L2_TUNER_MODE_MONO);
- if (chip->prevmode != V4L2_TUNER_MODE_MONO)
- chip->prevmode = -1; /* reset previous mode */
- mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
+ if (desc->flags & CHIP_HAS_BASSTREBLE) {
+ if (!desc->bassfunc || !desc->treblefunc) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without bass/treble controls
+ */
+ v4l2_info(sd, "bass/treble callbacks undefined!\n");
+ desc->flags &= ~CHIP_HAS_BASSTREBLE;
+ } else {
+ chip->treble = desc->trebleinit ?
+ desc->trebleinit : 32768;
+ chip->bass = desc->bassinit ?
+ desc->bassinit : 32768;
+ chip_write(chip, desc->bassreg,
+ desc->bassfunc(chip->bass));
+ chip_write(chip, desc->treblereg,
+ desc->treblefunc(chip->treble));
}
- break;
+ }
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_TVAUDIO, 0);
+ chip->thread = NULL;
+ if (desc->flags & CHIP_NEED_CHECKMODE) {
+ if (!desc->getmode || !desc->setmode) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without kthread
+ */
+ v4l2_info(sd, "set/get mode callbacks undefined!\n");
+ return 0;
+ }
+ /* start async thread */
+ init_timer(&chip->wt);
+ chip->wt.function = chip_thread_wake;
+ chip->wt.data = (unsigned long)chip;
+ chip->thread = kthread_run(chip_thread, chip, client->name);
+ if (IS_ERR(chip->thread)) {
+ v4l2_warn(sd, "failed to create kthread\n");
+ chip->thread = NULL;
+ }
}
return 0;
}
-static int chip_legacy_probe(struct i2c_adapter *adap)
+static int tvaudio_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct CHIPSTATE *chip = to_state(sd);
+
+ del_timer_sync(&chip->wt);
+ if (chip->thread) {
+ /* shutdown async thread */
+ kthread_stop(chip->thread);
+ chip->thread = NULL;
+ }
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(chip);
+ return 0;
+}
+
+static int tvaudio_legacy_probe(struct i2c_adapter *adap)
{
/* don't attach on saa7146 based cards,
because dedicated drivers are used */
@@ -1894,18 +1953,18 @@ static int chip_legacy_probe(struct i2c_adapter *adap)
/* This driver supports many devices and the idea is to let the driver
detect which device is present. So rather than listing all supported
devices here, we pretend to support a single, fake device type. */
-static const struct i2c_device_id chip_id[] = {
+static const struct i2c_device_id tvaudio_id[] = {
{ "tvaudio", 0 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, chip_id);
+MODULE_DEVICE_TABLE(i2c, tvaudio_id);
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "tvaudio",
.driverid = I2C_DRIVERID_TVAUDIO,
- .command = chip_command,
- .probe = chip_probe,
- .remove = chip_remove,
- .legacy_probe = chip_legacy_probe,
- .id_table = chip_id,
+ .command = tvaudio_command,
+ .probe = tvaudio_probe,
+ .remove = tvaudio_remove,
+ .legacy_probe = tvaudio_legacy_probe,
+ .id_table = tvaudio_id,
};
diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c
new file mode 100644
index 000000000000..ac9aa40d09f6
--- /dev/null
+++ b/drivers/media/video/tvp514x.c
@@ -0,0 +1,1569 @@
+/*
+ * drivers/media/video/tvp514x.c
+ *
+ * TI TVP5146/47 decoder driver
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-int-device.h>
+#include <media/tvp514x.h>
+
+#include "tvp514x_regs.h"
+
+/* Module Name */
+#define TVP514X_MODULE_NAME "tvp514x"
+
+/* Private macros for TVP */
+#define I2C_RETRY_COUNT (5)
+#define LOCK_RETRY_COUNT (5)
+#define LOCK_RETRY_DELAY (200)
+
+/* Debug functions */
+static int debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dump_reg(client, reg, val) \
+ do { \
+ val = tvp514x_read_reg(client, reg); \
+ v4l_info(client, "Reg(0x%.2X): 0x%.2X\n", reg, val); \
+ } while (0)
+
+/**
+ * enum tvp514x_std - enum for supported standards
+ */
+enum tvp514x_std {
+ STD_NTSC_MJ = 0,
+ STD_PAL_BDGHIN,
+ STD_INVALID
+};
+
+/**
+ * enum tvp514x_state - enum for different decoder states
+ */
+enum tvp514x_state {
+ STATE_NOT_DETECTED,
+ STATE_DETECTED
+};
+
+/**
+ * struct tvp514x_std_info - Structure to store standard informations
+ * @width: Line width in pixels
+ * @height:Number of active lines
+ * @video_std: Value to write in REG_VIDEO_STD register
+ * @standard: v4l2 standard structure information
+ */
+struct tvp514x_std_info {
+ unsigned long width;
+ unsigned long height;
+ u8 video_std;
+ struct v4l2_standard standard;
+};
+
+/**
+ * struct tvp514x_decoded - TVP5146/47 decoder object
+ * @v4l2_int_device: Slave handle
+ * @pdata: Board specific
+ * @client: I2C client data
+ * @id: Entry from I2C table
+ * @ver: Chip version
+ * @state: TVP5146/47 decoder state - detected or not-detected
+ * @pix: Current pixel format
+ * @num_fmts: Number of formats
+ * @fmt_list: Format list
+ * @current_std: Current standard
+ * @num_stds: Number of standards
+ * @std_list: Standards list
+ * @route: input and output routing at chip level
+ */
+struct tvp514x_decoder {
+ struct v4l2_int_device *v4l2_int_device;
+ const struct tvp514x_platform_data *pdata;
+ struct i2c_client *client;
+
+ struct i2c_device_id *id;
+
+ int ver;
+ enum tvp514x_state state;
+
+ struct v4l2_pix_format pix;
+ int num_fmts;
+ const struct v4l2_fmtdesc *fmt_list;
+
+ enum tvp514x_std current_std;
+ int num_stds;
+ struct tvp514x_std_info *std_list;
+
+ struct v4l2_routing route;
+};
+
+/* TVP514x default register values */
+static struct tvp514x_reg tvp514x_reg_list[] = {
+ {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */
+ {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F},
+ {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+ {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F},
+ {TOK_WRITE, REG_COLOR_KILLER, 0x10},
+ {TOK_WRITE, REG_LUMA_CONTROL1, 0x00},
+ {TOK_WRITE, REG_LUMA_CONTROL2, 0x00},
+ {TOK_WRITE, REG_LUMA_CONTROL3, 0x02},
+ {TOK_WRITE, REG_BRIGHTNESS, 0x80},
+ {TOK_WRITE, REG_CONTRAST, 0x80},
+ {TOK_WRITE, REG_SATURATION, 0x80},
+ {TOK_WRITE, REG_HUE, 0x00},
+ {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00},
+ {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E},
+ {TOK_SKIP, 0x0F, 0x00}, /* Reserved */
+ {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80},
+ {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80},
+ {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80},
+ {TOK_SKIP, 0x13, 0x00}, /* Reserved */
+ {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80},
+ {TOK_SKIP, 0x15, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, /* NTSC timing */
+ {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25},
+ {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03},
+ {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, /* NTSC timing */
+ {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40},
+ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, /* NTSC timing */
+ {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07},
+ {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, /* NTSC timing */
+ {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15},
+ {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00},
+ {TOK_SKIP, 0x26, 0x00}, /* Reserved */
+ {TOK_SKIP, 0x27, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC},
+ {TOK_SKIP, 0x29, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00},
+ {TOK_SKIP, 0x2B, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_SCART_DELAY, 0x00},
+ {TOK_SKIP, REG_CTI_DELAY, 0x00},
+ {TOK_SKIP, REG_CTI_CONTROL, 0x00},
+ {TOK_SKIP, 0x2F, 0x00}, /* Reserved */
+ {TOK_SKIP, 0x30, 0x00}, /* Reserved */
+ {TOK_SKIP, 0x31, 0x00}, /* Reserved */
+ {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, /* HS, VS active high */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, /* 10-bit BT.656 */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, /* Enable clk & data */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, /* Enable AVID & FLD */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, /* Enable VS & HS */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF},
+ {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF},
+ {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, /* Clear status */
+ {TOK_TERM, 0, 0},
+};
+
+/* List of image formats supported by TVP5146/47 decoder
+ * Currently we are using 8 bit mode only, but can be
+ * extended to 10/20 bit mode.
+ */
+static const struct v4l2_fmtdesc tvp514x_fmt_list[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = 0,
+ .description = "8-bit UYVY 4:2:2 Format",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ },
+};
+
+/*
+ * Supported standards -
+ *
+ * Currently supports two standards only, need to add support for rest of the
+ * modes, like SECAM, etc...
+ */
+static struct tvp514x_std_info tvp514x_std_list[] = {
+ /* Standard: STD_NTSC_MJ */
+ [STD_NTSC_MJ] = {
+ .width = NTSC_NUM_ACTIVE_PIXELS,
+ .height = NTSC_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STD_NTSC_MJ_BIT,
+ .standard = {
+ .index = 0,
+ .id = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .frameperiod = {1001, 30000},
+ .framelines = 525
+ },
+ /* Standard: STD_PAL_BDGHIN */
+ },
+ [STD_PAL_BDGHIN] = {
+ .width = PAL_NUM_ACTIVE_PIXELS,
+ .height = PAL_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STD_PAL_BDGHIN_BIT,
+ .standard = {
+ .index = 1,
+ .id = V4L2_STD_PAL,
+ .name = "PAL",
+ .frameperiod = {1, 25},
+ .framelines = 625
+ },
+ },
+ /* Standard: need to add for additional standard */
+};
+/*
+ * Control structure for Auto Gain
+ * This is temporary data, will get replaced once
+ * v4l2_ctrl_query_fill supports it.
+ */
+static const struct v4l2_queryctrl tvp514x_autogain_ctrl = {
+ .id = V4L2_CID_AUTOGAIN,
+ .name = "Gain, Automatic",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+};
+
+/*
+ * Read a value from a register in an TVP5146/47 decoder device.
+ * Returns value read if successful, or non-zero (-1) otherwise.
+ */
+static int tvp514x_read_reg(struct i2c_client *client, u8 reg)
+{
+ int err;
+ int retry = 0;
+read_again:
+
+ err = i2c_smbus_read_byte_data(client, reg);
+ if (err == -1) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l_warn(client, "Read: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto read_again;
+ }
+ }
+
+ return err;
+}
+
+/*
+ * Write a value to a register in an TVP5146/47 decoder device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ int err;
+ int retry = 0;
+write_again:
+
+ err = i2c_smbus_write_byte_data(client, reg, val);
+ if (err) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l_warn(client, "Write: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto write_again;
+ }
+ }
+
+ return err;
+}
+
+/*
+ * tvp514x_write_regs : Initializes a list of TVP5146/47 registers
+ * if token is TOK_TERM, then entire write operation terminates
+ * if token is TOK_DELAY, then a delay of 'val' msec is introduced
+ * if token is TOK_SKIP, then the register write is skipped
+ * if token is TOK_WRITE, then the register write is performed
+ *
+ * reglist - list of registers to be written
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_write_regs(struct i2c_client *client,
+ const struct tvp514x_reg reglist[])
+{
+ int err;
+ const struct tvp514x_reg *next = reglist;
+
+ for (; next->token != TOK_TERM; next++) {
+ if (next->token == TOK_DELAY) {
+ msleep(next->val);
+ continue;
+ }
+
+ if (next->token == TOK_SKIP)
+ continue;
+
+ err = tvp514x_write_reg(client, next->reg, (u8) next->val);
+ if (err) {
+ v4l_err(client, "Write failed. Err[%d]\n", err);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * tvp514x_get_current_std:
+ * Returns the current standard detected by TVP5146/47
+ */
+static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder
+ *decoder)
+{
+ u8 std, std_status;
+
+ std = tvp514x_read_reg(decoder->client, REG_VIDEO_STD);
+ if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) {
+ /* use the standard status register */
+ std_status = tvp514x_read_reg(decoder->client,
+ REG_VIDEO_STD_STATUS);
+ } else
+ std_status = std; /* use the standard register itself */
+
+ switch (std_status & VIDEO_STD_MASK) {
+ case VIDEO_STD_NTSC_MJ_BIT:
+ return STD_NTSC_MJ;
+
+ case VIDEO_STD_PAL_BDGHIN_BIT:
+ return STD_PAL_BDGHIN;
+
+ default:
+ return STD_INVALID;
+ }
+
+ return STD_INVALID;
+}
+
+/*
+ * TVP5146/47 register dump function
+ */
+static void tvp514x_reg_dump(struct tvp514x_decoder *decoder)
+{
+ u8 value;
+
+ dump_reg(decoder->client, REG_INPUT_SEL, value);
+ dump_reg(decoder->client, REG_AFE_GAIN_CTRL, value);
+ dump_reg(decoder->client, REG_VIDEO_STD, value);
+ dump_reg(decoder->client, REG_OPERATION_MODE, value);
+ dump_reg(decoder->client, REG_COLOR_KILLER, value);
+ dump_reg(decoder->client, REG_LUMA_CONTROL1, value);
+ dump_reg(decoder->client, REG_LUMA_CONTROL2, value);
+ dump_reg(decoder->client, REG_LUMA_CONTROL3, value);
+ dump_reg(decoder->client, REG_BRIGHTNESS, value);
+ dump_reg(decoder->client, REG_CONTRAST, value);
+ dump_reg(decoder->client, REG_SATURATION, value);
+ dump_reg(decoder->client, REG_HUE, value);
+ dump_reg(decoder->client, REG_CHROMA_CONTROL1, value);
+ dump_reg(decoder->client, REG_CHROMA_CONTROL2, value);
+ dump_reg(decoder->client, REG_COMP_PR_SATURATION, value);
+ dump_reg(decoder->client, REG_COMP_Y_CONTRAST, value);
+ dump_reg(decoder->client, REG_COMP_PB_SATURATION, value);
+ dump_reg(decoder->client, REG_COMP_Y_BRIGHTNESS, value);
+ dump_reg(decoder->client, REG_AVID_START_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_AVID_START_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_AVID_STOP_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_AVID_STOP_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_HSYNC_START_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_HSYNC_START_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_VSYNC_START_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VSYNC_START_LINE_MSB, value);
+ dump_reg(decoder->client, REG_VSYNC_STOP_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VSYNC_STOP_LINE_MSB, value);
+ dump_reg(decoder->client, REG_VBLK_START_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VBLK_START_LINE_MSB, value);
+ dump_reg(decoder->client, REG_VBLK_STOP_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VBLK_STOP_LINE_MSB, value);
+ dump_reg(decoder->client, REG_SYNC_CONTROL, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER1, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER2, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER3, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER4, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER5, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER6, value);
+ dump_reg(decoder->client, REG_CLEAR_LOST_LOCK, value);
+}
+
+/*
+ * Configure the TVP5146/47 with the current register settings
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_configure(struct tvp514x_decoder *decoder)
+{
+ int err;
+
+ /* common register initialization */
+ err =
+ tvp514x_write_regs(decoder->client, tvp514x_reg_list);
+ if (err)
+ return err;
+
+ if (debug)
+ tvp514x_reg_dump(decoder);
+
+ return 0;
+}
+
+/*
+ * Detect if an tvp514x is present, and if so which revision.
+ * A device is considered to be detected if the chip ID (LSB and MSB)
+ * registers match the expected values.
+ * Any value of the rom version register is accepted.
+ * Returns ENODEV error number if no device is detected, or zero
+ * if a device is detected.
+ */
+static int tvp514x_detect(struct tvp514x_decoder *decoder)
+{
+ u8 chip_id_msb, chip_id_lsb, rom_ver;
+
+ chip_id_msb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_MSB);
+ chip_id_lsb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_LSB);
+ rom_ver = tvp514x_read_reg(decoder->client, REG_ROM_VERSION);
+
+ v4l_dbg(1, debug, decoder->client,
+ "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n",
+ chip_id_msb, chip_id_lsb, rom_ver);
+ if ((chip_id_msb != TVP514X_CHIP_ID_MSB)
+ || ((chip_id_lsb != TVP5146_CHIP_ID_LSB)
+ && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) {
+ /* We didn't read the values we expected, so this must not be
+ * an TVP5146/47.
+ */
+ v4l_err(decoder->client,
+ "chip id mismatch msb:0x%x lsb:0x%x\n",
+ chip_id_msb, chip_id_lsb);
+ return -ENODEV;
+ }
+
+ decoder->ver = rom_ver;
+ decoder->state = STATE_DETECTED;
+
+ v4l_info(decoder->client,
+ "%s found at 0x%x (%s)\n", decoder->client->name,
+ decoder->client->addr << 1,
+ decoder->client->adapter->name);
+ return 0;
+}
+
+/*
+ * Following are decoder interface functions implemented by
+ * TVP5146/47 decoder driver.
+ */
+
+/**
+ * ioctl_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @std_id: standard V4L2 std_id ioctl enum
+ *
+ * Returns the current standard detected by TVP5146/47. If no active input is
+ * detected, returns -EINVAL
+ */
+static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ enum tvp514x_std current_std;
+ enum tvp514x_input input_sel;
+ u8 sync_lock_status, lock_mask;
+
+ if (std_id == NULL)
+ return -EINVAL;
+
+ /* get the current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ input_sel = decoder->route.input;
+
+ switch (input_sel) {
+ case INPUT_CVBS_VI1A:
+ case INPUT_CVBS_VI1B:
+ case INPUT_CVBS_VI1C:
+ case INPUT_CVBS_VI2A:
+ case INPUT_CVBS_VI2B:
+ case INPUT_CVBS_VI2C:
+ case INPUT_CVBS_VI3A:
+ case INPUT_CVBS_VI3B:
+ case INPUT_CVBS_VI3C:
+ case INPUT_CVBS_VI4A:
+ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
+ STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+
+ case INPUT_SVIDEO_VI2A_VI1A:
+ case INPUT_SVIDEO_VI2B_VI1B:
+ case INPUT_SVIDEO_VI2C_VI1C:
+ case INPUT_SVIDEO_VI2A_VI3A:
+ case INPUT_SVIDEO_VI2B_VI3B:
+ case INPUT_SVIDEO_VI2C_VI3C:
+ case INPUT_SVIDEO_VI4A_VI1A:
+ case INPUT_SVIDEO_VI4A_VI1B:
+ case INPUT_SVIDEO_VI4A_VI1C:
+ case INPUT_SVIDEO_VI4A_VI3A:
+ case INPUT_SVIDEO_VI4A_VI3B:
+ case INPUT_SVIDEO_VI4A_VI3C:
+ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+ /*Need to add other interfaces*/
+ default:
+ return -EINVAL;
+ }
+ /* check whether signal is locked */
+ sync_lock_status = tvp514x_read_reg(decoder->client, REG_STATUS1);
+ if (lock_mask != (sync_lock_status & lock_mask))
+ return -EINVAL; /* No input detected */
+
+ decoder->current_std = current_std;
+ *std_id = decoder->std_list[current_std].standard.id;
+
+ v4l_dbg(1, debug, decoder->client, "Current STD: %s",
+ decoder->std_list[current_std].standard.name);
+ return 0;
+}
+
+/**
+ * ioctl_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @std_id: standard V4L2 v4l2_std_id ioctl enum
+ *
+ * If std_id is supported, sets the requested standard. Otherwise, returns
+ * -EINVAL
+ */
+static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err, i;
+
+ if (std_id == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < decoder->num_stds; i++)
+ if (*std_id & decoder->std_list[i].standard.id)
+ break;
+
+ if ((i == decoder->num_stds) || (i == STD_INVALID))
+ return -EINVAL;
+
+ err = tvp514x_write_reg(decoder->client, REG_VIDEO_STD,
+ decoder->std_list[i].video_std);
+ if (err)
+ return err;
+
+ decoder->current_std = i;
+ tvp514x_reg_list[REG_VIDEO_STD].val = decoder->std_list[i].video_std;
+
+ v4l_dbg(1, debug, decoder->client, "Standard set to: %s",
+ decoder->std_list[i].standard.name);
+ return 0;
+}
+
+/**
+ * ioctl_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @index: number of the input
+ *
+ * If index is valid, selects the requested input. Otherwise, returns -EINVAL if
+ * the input is not supported or there is no active signal present in the
+ * selected input.
+ */
+static int ioctl_s_routing(struct v4l2_int_device *s,
+ struct v4l2_routing *route)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err;
+ enum tvp514x_input input_sel;
+ enum tvp514x_output output_sel;
+ enum tvp514x_std current_std = STD_INVALID;
+ u8 sync_lock_status, lock_mask;
+ int try_count = LOCK_RETRY_COUNT;
+
+ if ((!route) || (route->input >= INPUT_INVALID) ||
+ (route->output >= OUTPUT_INVALID))
+ return -EINVAL; /* Index out of bound */
+
+ input_sel = route->input;
+ output_sel = route->output;
+
+ err = tvp514x_write_reg(decoder->client, REG_INPUT_SEL, input_sel);
+ if (err)
+ return err;
+
+ output_sel |= tvp514x_read_reg(decoder->client,
+ REG_OUTPUT_FORMATTER1) & 0x7;
+ err = tvp514x_write_reg(decoder->client, REG_OUTPUT_FORMATTER1,
+ output_sel);
+ if (err)
+ return err;
+
+ tvp514x_reg_list[REG_INPUT_SEL].val = input_sel;
+ tvp514x_reg_list[REG_OUTPUT_FORMATTER1].val = output_sel;
+
+ /* Clear status */
+ msleep(LOCK_RETRY_DELAY);
+ err =
+ tvp514x_write_reg(decoder->client, REG_CLEAR_LOST_LOCK, 0x01);
+ if (err)
+ return err;
+
+ switch (input_sel) {
+ case INPUT_CVBS_VI1A:
+ case INPUT_CVBS_VI1B:
+ case INPUT_CVBS_VI1C:
+ case INPUT_CVBS_VI2A:
+ case INPUT_CVBS_VI2B:
+ case INPUT_CVBS_VI2C:
+ case INPUT_CVBS_VI3A:
+ case INPUT_CVBS_VI3B:
+ case INPUT_CVBS_VI3C:
+ case INPUT_CVBS_VI4A:
+ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
+ STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+
+ case INPUT_SVIDEO_VI2A_VI1A:
+ case INPUT_SVIDEO_VI2B_VI1B:
+ case INPUT_SVIDEO_VI2C_VI1C:
+ case INPUT_SVIDEO_VI2A_VI3A:
+ case INPUT_SVIDEO_VI2B_VI3B:
+ case INPUT_SVIDEO_VI2C_VI3C:
+ case INPUT_SVIDEO_VI4A_VI1A:
+ case INPUT_SVIDEO_VI4A_VI1B:
+ case INPUT_SVIDEO_VI4A_VI1C:
+ case INPUT_SVIDEO_VI4A_VI3A:
+ case INPUT_SVIDEO_VI4A_VI3B:
+ case INPUT_SVIDEO_VI4A_VI3C:
+ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+ /*Need to add other interfaces*/
+ default:
+ return -EINVAL;
+ }
+
+ while (try_count-- > 0) {
+ /* Allow decoder to sync up with new input */
+ msleep(LOCK_RETRY_DELAY);
+
+ /* get the current standard for future reference */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ continue;
+
+ sync_lock_status = tvp514x_read_reg(decoder->client,
+ REG_STATUS1);
+ if (lock_mask == (sync_lock_status & lock_mask))
+ break; /* Input detected */
+ }
+
+ if ((current_std == STD_INVALID) || (try_count < 0))
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+ decoder->route.input = route->input;
+ decoder->route.output = route->output;
+
+ v4l_dbg(1, debug, decoder->client,
+ "Input set to: %d, std : %d",
+ input_sel, current_std);
+
+ return 0;
+}
+
+/**
+ * ioctl_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @qctrl: standard V4L2 v4l2_queryctrl structure
+ *
+ * If the requested control is supported, returns the control information.
+ * Otherwise, returns -EINVAL if the control is not supported.
+ */
+static int
+ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err = -EINVAL;
+
+ if (qctrl == NULL)
+ return err;
+
+ switch (qctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ /* Brightness supported is same as standard one (0-255),
+ * so make use of standard API provided.
+ */
+ err = v4l2_ctrl_query_fill_std(qctrl);
+ break;
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ /* Saturation and Contrast supported is -
+ * Contrast: 0 - 255 (Default - 128)
+ * Saturation: 0 - 255 (Default - 128)
+ */
+ err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128);
+ break;
+ case V4L2_CID_HUE:
+ /* Hue Supported is -
+ * Hue - -180 - +180 (Default - 0, Step - +180)
+ */
+ err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ /* Autogain is either 0 or 1*/
+ memcpy(qctrl, &tvp514x_autogain_ctrl,
+ sizeof(struct v4l2_queryctrl));
+ err = 0;
+ break;
+ default:
+ v4l_err(decoder->client,
+ "invalid control id %d\n", qctrl->id);
+ return err;
+ }
+
+ v4l_dbg(1, debug, decoder->client,
+ "Query Control: %s : Min - %d, Max - %d, Def - %d",
+ qctrl->name,
+ qctrl->minimum,
+ qctrl->maximum,
+ qctrl->default_value);
+
+ return err;
+}
+
+/**
+ * ioctl_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @ctrl: pointer to v4l2_control structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the decoder. Otherwise, returns -EINVAL if the control is not
+ * supported.
+ */
+static int
+ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ if (ctrl == NULL)
+ return -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = tvp514x_reg_list[REG_BRIGHTNESS].val;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = tvp514x_reg_list[REG_CONTRAST].val;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = tvp514x_reg_list[REG_SATURATION].val;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = tvp514x_reg_list[REG_HUE].val;
+ if (ctrl->value == 0x7F)
+ ctrl->value = 180;
+ else if (ctrl->value == 0x80)
+ ctrl->value = -180;
+ else
+ ctrl->value = 0;
+
+ break;
+ case V4L2_CID_AUTOGAIN:
+ ctrl->value = tvp514x_reg_list[REG_AFE_GAIN_CTRL].val;
+ if ((ctrl->value & 0x3) == 3)
+ ctrl->value = 1;
+ else
+ ctrl->value = 0;
+
+ break;
+ default:
+ v4l_err(decoder->client,
+ "invalid control id %d\n", ctrl->id);
+ return -EINVAL;
+ }
+
+ v4l_dbg(1, debug, decoder->client,
+ "Get Control: ID - %d - %d",
+ ctrl->id, ctrl->value);
+ return 0;
+}
+
+/**
+ * ioctl_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @ctrl: pointer to v4l2_control structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW. Otherwise, returns -EINVAL if the control is not supported.
+ */
+static int
+ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err = -EINVAL, value;
+
+ if (ctrl == NULL)
+ return err;
+
+ value = (__s32) ctrl->value;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l_err(decoder->client,
+ "invalid brightness setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_BRIGHTNESS,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_BRIGHTNESS].val = value;
+ break;
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l_err(decoder->client,
+ "invalid contrast setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_CONTRAST,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_CONTRAST].val = value;
+ break;
+ case V4L2_CID_SATURATION:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l_err(decoder->client,
+ "invalid saturation setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_SATURATION,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_SATURATION].val = value;
+ break;
+ case V4L2_CID_HUE:
+ if (value == 180)
+ value = 0x7F;
+ else if (value == -180)
+ value = 0x80;
+ else if (value == 0)
+ value = 0;
+ else {
+ v4l_err(decoder->client,
+ "invalid hue setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_HUE,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_HUE].val = value;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (value == 1)
+ value = 0x0F;
+ else if (value == 0)
+ value = 0x0C;
+ else {
+ v4l_err(decoder->client,
+ "invalid auto gain setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_AFE_GAIN_CTRL,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_AFE_GAIN_CTRL].val = value;
+ break;
+ default:
+ v4l_err(decoder->client,
+ "invalid control id %d\n", ctrl->id);
+ return err;
+ }
+
+ v4l_dbg(1, debug, decoder->client,
+ "Set Control: ID - %d - %d",
+ ctrl->id, ctrl->value);
+
+ return err;
+}
+
+/**
+ * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure
+ *
+ * Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats
+ */
+static int
+ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int index;
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ index = fmt->index;
+ if ((index >= decoder->num_fmts) || (index < 0))
+ return -EINVAL; /* Index out of bound */
+
+ if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ memcpy(fmt, &decoder->fmt_list[index],
+ sizeof(struct v4l2_fmtdesc));
+
+ v4l_dbg(1, debug, decoder->client,
+ "Current FMT: index - %d (%s)",
+ decoder->fmt_list[index].index,
+ decoder->fmt_list[index].description);
+ return 0;
+}
+
+/**
+ * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
+ *
+ * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This
+ * ioctl is used to negotiate the image capture size and pixel format
+ * without actually making it take effect.
+ */
+static int
+ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int ifmt;
+ struct v4l2_pix_format *pix;
+ enum tvp514x_std current_std;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ pix = &f->fmt.pix;
+
+ /* Calculate height and width based on current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+ pix->width = decoder->std_list[current_std].width;
+ pix->height = decoder->std_list[current_std].height;
+
+ for (ifmt = 0; ifmt < decoder->num_fmts; ifmt++) {
+ if (pix->pixelformat ==
+ decoder->fmt_list[ifmt].pixelformat)
+ break;
+ }
+ if (ifmt == decoder->num_fmts)
+ ifmt = 0; /* None of the format matched, select default */
+ pix->pixelformat = decoder->fmt_list[ifmt].pixelformat;
+
+ pix->field = V4L2_FIELD_INTERLACED;
+ pix->bytesperline = pix->width * 2;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pix->priv = 0;
+
+ v4l_dbg(1, debug, decoder->client,
+ "Try FMT: pixelformat - %s, bytesperline - %d"
+ "Width - %d, Height - %d",
+ decoder->fmt_list[ifmt].description, pix->bytesperline,
+ pix->width, pix->height);
+ return 0;
+}
+
+/**
+ * ioctl_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
+ *
+ * If the requested format is supported, configures the HW to use that
+ * format, returns error code if format not supported or HW can't be
+ * correctly configured.
+ */
+static int
+ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ struct v4l2_pix_format *pix;
+ int rval;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ pix = &f->fmt.pix;
+ rval = ioctl_try_fmt_cap(s, f);
+ if (rval)
+ return rval;
+
+ decoder->pix = *pix;
+
+ return rval;
+}
+
+/**
+ * ioctl_g_fmt_cap - V4L2 decoder interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the decoder's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int
+ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ f->fmt.pix = decoder->pix;
+
+ v4l_dbg(1, debug, decoder->client,
+ "Current FMT: bytesperline - %d"
+ "Width - %d, Height - %d",
+ decoder->pix.bytesperline,
+ decoder->pix.width, decoder->pix.height);
+ return 0;
+}
+
+/**
+ * ioctl_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the decoder's video CAPTURE parameters.
+ */
+static int
+ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ struct v4l2_captureparm *cparm;
+ enum tvp514x_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* get the current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+
+ cparm = &a->parm.capture;
+ cparm->capability = V4L2_CAP_TIMEPERFRAME;
+ cparm->timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * ioctl_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the decoder to use the input parameters, if possible. If
+ * not possible, returns the appropriate error code.
+ */
+static int
+ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ struct v4l2_fract *timeperframe;
+ enum tvp514x_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ timeperframe = &a->parm.capture.timeperframe;
+
+ /* get the current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+
+ *timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * ioctl_g_ifparm - V4L2 decoder interface handler for vidioc_int_g_ifparm_num
+ * @s: pointer to standard V4L2 device structure
+ * @p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p. This value is returned in the p
+ * parameter.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int rval;
+
+ if (p == NULL)
+ return -EINVAL;
+
+ if (NULL == decoder->pdata->ifparm)
+ return -EINVAL;
+
+ rval = decoder->pdata->ifparm(p);
+ if (rval) {
+ v4l_err(decoder->client, "g_ifparm.Err[%d]\n", rval);
+ return rval;
+ }
+
+ p->u.bt656.clock_curr = TVP514X_XCLK_BT656;
+
+ return 0;
+}
+
+/**
+ * ioctl_g_priv - V4L2 decoder interface handler for vidioc_int_g_priv_num
+ * @s: pointer to standard V4L2 device structure
+ * @p: void pointer to hold decoder's private data address
+ *
+ * Returns device's (decoder's) private data area address in p parameter
+ */
+static int ioctl_g_priv(struct v4l2_int_device *s, void *p)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ if (NULL == decoder->pdata->priv_data_set)
+ return -EINVAL;
+
+ return decoder->pdata->priv_data_set(p);
+}
+
+/**
+ * ioctl_s_power - V4L2 decoder interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err = 0;
+
+ switch (on) {
+ case V4L2_POWER_OFF:
+ /* Power Down Sequence */
+ err =
+ tvp514x_write_reg(decoder->client, REG_OPERATION_MODE,
+ 0x01);
+ /* Disable mux for TVP5146/47 decoder data path */
+ if (decoder->pdata->power_set)
+ err |= decoder->pdata->power_set(on);
+ decoder->state = STATE_NOT_DETECTED;
+ break;
+
+ case V4L2_POWER_STANDBY:
+ if (decoder->pdata->power_set)
+ err = decoder->pdata->power_set(on);
+ break;
+
+ case V4L2_POWER_ON:
+ /* Enable mux for TVP5146/47 decoder data path */
+ if ((decoder->pdata->power_set) &&
+ (decoder->state == STATE_NOT_DETECTED)) {
+ int i;
+ struct tvp514x_init_seq *int_seq =
+ (struct tvp514x_init_seq *)
+ decoder->id->driver_data;
+
+ err = decoder->pdata->power_set(on);
+
+ /* Power Up Sequence */
+ for (i = 0; i < int_seq->no_regs; i++) {
+ err |= tvp514x_write_reg(decoder->client,
+ int_seq->init_reg_seq[i].reg,
+ int_seq->init_reg_seq[i].val);
+ }
+ /* Detect the sensor is not already detected */
+ err |= tvp514x_detect(decoder);
+ if (err) {
+ v4l_err(decoder->client,
+ "Unable to detect decoder\n");
+ return err;
+ }
+ }
+ err |= tvp514x_configure(decoder);
+ break;
+
+ default:
+ err = -ENODEV;
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * ioctl_init - V4L2 decoder interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialize the decoder device (calls tvp514x_configure())
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ /* Set default standard to auto */
+ tvp514x_reg_list[REG_VIDEO_STD].val =
+ VIDEO_STD_AUTO_SWITCH_BIT;
+
+ return tvp514x_configure(decoder);
+}
+
+/**
+ * ioctl_dev_exit - V4L2 decoder interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+ return 0;
+}
+
+/**
+ * ioctl_dev_init - V4L2 decoder interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master. Returns 0 if
+ * TVP5146/47 device could be found, otherwise returns appropriate error.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err;
+
+ err = tvp514x_detect(decoder);
+ if (err < 0) {
+ v4l_err(decoder->client,
+ "Unable to detect decoder\n");
+ return err;
+ }
+
+ v4l_info(decoder->client,
+ "chip version 0x%.2x detected\n", decoder->ver);
+
+ return 0;
+}
+
+static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = {
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*) ioctl_dev_init},
+ {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func*) ioctl_dev_exit},
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func*) ioctl_s_power},
+ {vidioc_int_g_priv_num, (v4l2_int_ioctl_func*) ioctl_g_priv},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm},
+ {vidioc_int_init_num, (v4l2_int_ioctl_func*) ioctl_init},
+ {vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap},
+ {vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_try_fmt_cap},
+ {vidioc_int_g_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_g_fmt_cap},
+ {vidioc_int_s_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_s_fmt_cap},
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm},
+ {vidioc_int_queryctrl_num,
+ (v4l2_int_ioctl_func *) ioctl_queryctrl},
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl},
+ {vidioc_int_querystd_num, (v4l2_int_ioctl_func *) ioctl_querystd},
+ {vidioc_int_s_std_num, (v4l2_int_ioctl_func *) ioctl_s_std},
+ {vidioc_int_s_video_routing_num,
+ (v4l2_int_ioctl_func *) ioctl_s_routing},
+};
+
+static struct v4l2_int_slave tvp514x_slave = {
+ .ioctls = tvp514x_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc),
+};
+
+static struct tvp514x_decoder tvp514x_dev = {
+ .state = STATE_NOT_DETECTED,
+
+ .fmt_list = tvp514x_fmt_list,
+ .num_fmts = ARRAY_SIZE(tvp514x_fmt_list),
+
+ .pix = { /* Default to NTSC 8-bit YUV 422 */
+ .width = NTSC_NUM_ACTIVE_PIXELS,
+ .height = NTSC_NUM_ACTIVE_LINES,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .field = V4L2_FIELD_INTERLACED,
+ .bytesperline = NTSC_NUM_ACTIVE_PIXELS * 2,
+ .sizeimage =
+ NTSC_NUM_ACTIVE_PIXELS * 2 * NTSC_NUM_ACTIVE_LINES,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ },
+
+ .current_std = STD_NTSC_MJ,
+ .std_list = tvp514x_std_list,
+ .num_stds = ARRAY_SIZE(tvp514x_std_list),
+
+};
+
+static struct v4l2_int_device tvp514x_int_device = {
+ .module = THIS_MODULE,
+ .name = TVP514X_MODULE_NAME,
+ .priv = &tvp514x_dev,
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &tvp514x_slave,
+ },
+};
+
+/**
+ * tvp514x_probe - decoder driver i2c probe handler
+ * @client: i2c driver client device structure
+ *
+ * Register decoder as an i2c client device and V4L2
+ * device.
+ */
+static int
+tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct tvp514x_decoder *decoder = &tvp514x_dev;
+ int err;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ decoder->pdata = client->dev.platform_data;
+ if (!decoder->pdata) {
+ v4l_err(client, "No platform data\n!!");
+ return -ENODEV;
+ }
+ /*
+ * Fetch platform specific data, and configure the
+ * tvp514x_reg_list[] accordingly. Since this is one
+ * time configuration, no need to preserve.
+ */
+ tvp514x_reg_list[REG_OUTPUT_FORMATTER2].val |=
+ (decoder->pdata->clk_polarity << 1);
+ tvp514x_reg_list[REG_SYNC_CONTROL].val |=
+ ((decoder->pdata->hs_polarity << 2) |
+ (decoder->pdata->vs_polarity << 3));
+ /*
+ * Save the id data, required for power up sequence
+ */
+ decoder->id = (struct i2c_device_id *)id;
+ /* Attach to Master */
+ strcpy(tvp514x_int_device.u.slave->attach_to, decoder->pdata->master);
+ decoder->v4l2_int_device = &tvp514x_int_device;
+ decoder->client = client;
+ i2c_set_clientdata(client, decoder);
+
+ /* Register with V4L2 layer as slave device */
+ err = v4l2_int_device_register(decoder->v4l2_int_device);
+ if (err) {
+ i2c_set_clientdata(client, NULL);
+ v4l_err(client,
+ "Unable to register to v4l2. Err[%d]\n", err);
+
+ } else
+ v4l_info(client, "Registered to v4l2 master %s!!\n",
+ decoder->pdata->master);
+
+ return 0;
+}
+
+/**
+ * tvp514x_remove - decoder driver i2c remove handler
+ * @client: i2c driver client device structure
+ *
+ * Unregister decoder as an i2c client device and V4L2
+ * device. Complement of tvp514x_probe().
+ */
+static int __exit tvp514x_remove(struct i2c_client *client)
+{
+ struct tvp514x_decoder *decoder = i2c_get_clientdata(client);
+
+ if (!client->adapter)
+ return -ENODEV; /* our client isn't attached */
+
+ v4l2_int_device_unregister(decoder->v4l2_int_device);
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+/*
+ * TVP5146 Init/Power on Sequence
+ */
+static const struct tvp514x_reg tvp5146_init_reg_seq[] = {
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+};
+static const struct tvp514x_init_seq tvp5146_init = {
+ .no_regs = ARRAY_SIZE(tvp5146_init_reg_seq),
+ .init_reg_seq = tvp5146_init_reg_seq,
+};
+/*
+ * TVP5147 Init/Power on Sequence
+ */
+static const struct tvp514x_reg tvp5147_init_reg_seq[] = {
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+};
+static const struct tvp514x_init_seq tvp5147_init = {
+ .no_regs = ARRAY_SIZE(tvp5147_init_reg_seq),
+ .init_reg_seq = tvp5147_init_reg_seq,
+};
+/*
+ * TVP5146M2/TVP5147M1 Init/Power on Sequence
+ */
+static const struct tvp514x_reg tvp514xm_init_reg_seq[] = {
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+};
+static const struct tvp514x_init_seq tvp514xm_init = {
+ .no_regs = ARRAY_SIZE(tvp514xm_init_reg_seq),
+ .init_reg_seq = tvp514xm_init_reg_seq,
+};
+/*
+ * I2C Device Table -
+ *
+ * name - Name of the actual device/chip.
+ * driver_data - Driver data
+ */
+static const struct i2c_device_id tvp514x_id[] = {
+ {"tvp5146", (unsigned long)&tvp5146_init},
+ {"tvp5146m2", (unsigned long)&tvp514xm_init},
+ {"tvp5147", (unsigned long)&tvp5147_init},
+ {"tvp5147m1", (unsigned long)&tvp514xm_init},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tvp514x_id);
+
+static struct i2c_driver tvp514x_i2c_driver = {
+ .driver = {
+ .name = TVP514X_MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = tvp514x_probe,
+ .remove = __exit_p(tvp514x_remove),
+ .id_table = tvp514x_id,
+};
+
+/**
+ * tvp514x_init
+ *
+ * Module init function
+ */
+static int __init tvp514x_init(void)
+{
+ return i2c_add_driver(&tvp514x_i2c_driver);
+}
+
+/**
+ * tvp514x_cleanup
+ *
+ * Module exit function
+ */
+static void __exit tvp514x_cleanup(void)
+{
+ i2c_del_driver(&tvp514x_i2c_driver);
+}
+
+module_init(tvp514x_init);
+module_exit(tvp514x_cleanup);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("TVP514X linux decoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tvp514x_regs.h b/drivers/media/video/tvp514x_regs.h
new file mode 100644
index 000000000000..351620aeecc2
--- /dev/null
+++ b/drivers/media/video/tvp514x_regs.h
@@ -0,0 +1,297 @@
+/*
+ * drivers/media/video/tvp514x_regs.h
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _TVP514X_REGS_H
+#define _TVP514X_REGS_H
+
+/*
+ * TVP5146/47 registers
+ */
+#define REG_INPUT_SEL (0x00)
+#define REG_AFE_GAIN_CTRL (0x01)
+#define REG_VIDEO_STD (0x02)
+#define REG_OPERATION_MODE (0x03)
+#define REG_AUTOSWITCH_MASK (0x04)
+
+#define REG_COLOR_KILLER (0x05)
+#define REG_LUMA_CONTROL1 (0x06)
+#define REG_LUMA_CONTROL2 (0x07)
+#define REG_LUMA_CONTROL3 (0x08)
+
+#define REG_BRIGHTNESS (0x09)
+#define REG_CONTRAST (0x0A)
+#define REG_SATURATION (0x0B)
+#define REG_HUE (0x0C)
+
+#define REG_CHROMA_CONTROL1 (0x0D)
+#define REG_CHROMA_CONTROL2 (0x0E)
+
+/* 0x0F Reserved */
+
+#define REG_COMP_PR_SATURATION (0x10)
+#define REG_COMP_Y_CONTRAST (0x11)
+#define REG_COMP_PB_SATURATION (0x12)
+
+/* 0x13 Reserved */
+
+#define REG_COMP_Y_BRIGHTNESS (0x14)
+
+/* 0x15 Reserved */
+
+#define REG_AVID_START_PIXEL_LSB (0x16)
+#define REG_AVID_START_PIXEL_MSB (0x17)
+#define REG_AVID_STOP_PIXEL_LSB (0x18)
+#define REG_AVID_STOP_PIXEL_MSB (0x19)
+
+#define REG_HSYNC_START_PIXEL_LSB (0x1A)
+#define REG_HSYNC_START_PIXEL_MSB (0x1B)
+#define REG_HSYNC_STOP_PIXEL_LSB (0x1C)
+#define REG_HSYNC_STOP_PIXEL_MSB (0x1D)
+
+#define REG_VSYNC_START_LINE_LSB (0x1E)
+#define REG_VSYNC_START_LINE_MSB (0x1F)
+#define REG_VSYNC_STOP_LINE_LSB (0x20)
+#define REG_VSYNC_STOP_LINE_MSB (0x21)
+
+#define REG_VBLK_START_LINE_LSB (0x22)
+#define REG_VBLK_START_LINE_MSB (0x23)
+#define REG_VBLK_STOP_LINE_LSB (0x24)
+#define REG_VBLK_STOP_LINE_MSB (0x25)
+
+/* 0x26 - 0x27 Reserved */
+
+#define REG_FAST_SWTICH_CONTROL (0x28)
+
+/* 0x29 Reserved */
+
+#define REG_FAST_SWTICH_SCART_DELAY (0x2A)
+
+/* 0x2B Reserved */
+
+#define REG_SCART_DELAY (0x2C)
+#define REG_CTI_DELAY (0x2D)
+#define REG_CTI_CONTROL (0x2E)
+
+/* 0x2F - 0x31 Reserved */
+
+#define REG_SYNC_CONTROL (0x32)
+#define REG_OUTPUT_FORMATTER1 (0x33)
+#define REG_OUTPUT_FORMATTER2 (0x34)
+#define REG_OUTPUT_FORMATTER3 (0x35)
+#define REG_OUTPUT_FORMATTER4 (0x36)
+#define REG_OUTPUT_FORMATTER5 (0x37)
+#define REG_OUTPUT_FORMATTER6 (0x38)
+#define REG_CLEAR_LOST_LOCK (0x39)
+
+#define REG_STATUS1 (0x3A)
+#define REG_STATUS2 (0x3B)
+
+#define REG_AGC_GAIN_STATUS_LSB (0x3C)
+#define REG_AGC_GAIN_STATUS_MSB (0x3D)
+
+/* 0x3E Reserved */
+
+#define REG_VIDEO_STD_STATUS (0x3F)
+#define REG_GPIO_INPUT1 (0x40)
+#define REG_GPIO_INPUT2 (0x41)
+
+/* 0x42 - 0x45 Reserved */
+
+#define REG_AFE_COARSE_GAIN_CH1 (0x46)
+#define REG_AFE_COARSE_GAIN_CH2 (0x47)
+#define REG_AFE_COARSE_GAIN_CH3 (0x48)
+#define REG_AFE_COARSE_GAIN_CH4 (0x49)
+
+#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A)
+#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B)
+#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C)
+#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D)
+#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E)
+#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F)
+#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50)
+#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51)
+
+/* 0x52 - 0x68 Reserved */
+
+#define REG_FBIT_VBIT_CONTROL1 (0x69)
+
+/* 0x6A - 0x6B Reserved */
+
+#define REG_BACKEND_AGC_CONTROL (0x6C)
+
+/* 0x6D - 0x6E Reserved */
+
+#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F)
+#define REG_ROM_VERSION (0x70)
+
+/* 0x71 - 0x73 Reserved */
+
+#define REG_AGC_WHITE_PEAK_PROCESSING (0x74)
+#define REG_FBIT_VBIT_CONTROL2 (0x75)
+#define REG_VCR_TRICK_MODE_CONTROL (0x76)
+#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77)
+#define REG_AGC_INCREMENT_SPEED (0x78)
+#define REG_AGC_INCREMENT_DELAY (0x79)
+
+/* 0x7A - 0x7F Reserved */
+
+#define REG_CHIP_ID_MSB (0x80)
+#define REG_CHIP_ID_LSB (0x81)
+
+/* 0x82 Reserved */
+
+#define REG_CPLL_SPEED_CONTROL (0x83)
+
+/* 0x84 - 0x96 Reserved */
+
+#define REG_STATUS_REQUEST (0x97)
+
+/* 0x98 - 0x99 Reserved */
+
+#define REG_VERTICAL_LINE_COUNT_LSB (0x9A)
+#define REG_VERTICAL_LINE_COUNT_MSB (0x9B)
+
+/* 0x9C - 0x9D Reserved */
+
+#define REG_AGC_DECREMENT_DELAY (0x9E)
+
+/* 0x9F - 0xB0 Reserved */
+
+#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1)
+#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2)
+#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3)
+#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4)
+#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5)
+#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6)
+#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7)
+#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8)
+#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9)
+#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA)
+#define REG_VDP_TTX_FILTER_CONTROL (0xBB)
+#define REG_VDP_FIFO_WORD_COUNT (0xBC)
+#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD)
+
+/* 0xBE Reserved */
+
+#define REG_VDP_FIFO_RESET (0xBF)
+#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0)
+#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1)
+#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2)
+#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3)
+
+/* 0xC4 - 0xD5 Reserved */
+
+#define REG_VDP_LINE_START (0xD6)
+#define REG_VDP_LINE_STOP (0xD7)
+#define REG_VDP_GLOBAL_LINE_MODE (0xD8)
+#define REG_VDP_FULL_FIELD_ENABLE (0xD9)
+#define REG_VDP_FULL_FIELD_MODE (0xDA)
+
+/* 0xDB - 0xDF Reserved */
+
+#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0)
+#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1)
+#define REG_FIFO_READ_DATA (0xE2)
+
+/* 0xE3 - 0xE7 Reserved */
+
+#define REG_VBUS_ADDRESS_ACCESS1 (0xE8)
+#define REG_VBUS_ADDRESS_ACCESS2 (0xE9)
+#define REG_VBUS_ADDRESS_ACCESS3 (0xEA)
+
+/* 0xEB - 0xEF Reserved */
+
+#define REG_INTERRUPT_RAW_STATUS0 (0xF0)
+#define REG_INTERRUPT_RAW_STATUS1 (0xF1)
+#define REG_INTERRUPT_STATUS0 (0xF2)
+#define REG_INTERRUPT_STATUS1 (0xF3)
+#define REG_INTERRUPT_MASK0 (0xF4)
+#define REG_INTERRUPT_MASK1 (0xF5)
+#define REG_INTERRUPT_CLEAR0 (0xF6)
+#define REG_INTERRUPT_CLEAR1 (0xF7)
+
+/* 0xF8 - 0xFF Reserved */
+
+/*
+ * Mask and bit definitions of TVP5146/47 registers
+ */
+/* The ID values we are looking for */
+#define TVP514X_CHIP_ID_MSB (0x51)
+#define TVP5146_CHIP_ID_LSB (0x46)
+#define TVP5147_CHIP_ID_LSB (0x47)
+
+#define VIDEO_STD_MASK (0x07)
+#define VIDEO_STD_AUTO_SWITCH_BIT (0x00)
+#define VIDEO_STD_NTSC_MJ_BIT (0x01)
+#define VIDEO_STD_PAL_BDGHIN_BIT (0x02)
+#define VIDEO_STD_PAL_M_BIT (0x03)
+#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04)
+#define VIDEO_STD_NTSC_4_43_BIT (0x05)
+#define VIDEO_STD_SECAM_BIT (0x06)
+#define VIDEO_STD_PAL_60_BIT (0x07)
+
+/*
+ * Status bit
+ */
+#define STATUS_TV_VCR_BIT (1<<0)
+#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1)
+#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2)
+#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3)
+#define STATUS_LOST_LOCK_DETECT_BIT (1<<4)
+#define STATUS_FEILD_RATE_BIT (1<<5)
+#define STATUS_LINE_ALTERNATING_BIT (1<<6)
+#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7)
+
+/* Tokens for register write */
+#define TOK_WRITE (0) /* token for write operation */
+#define TOK_TERM (1) /* terminating token */
+#define TOK_DELAY (2) /* delay token for reg list */
+#define TOK_SKIP (3) /* token to skip a register */
+/**
+ * struct tvp514x_reg - Structure for TVP5146/47 register initialization values
+ * @token - Token: TOK_WRITE, TOK_TERM etc..
+ * @reg - Register offset
+ * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY
+ */
+struct tvp514x_reg {
+ u8 token;
+ u8 reg;
+ u32 val;
+};
+
+/**
+ * struct tvp514x_init_seq - Structure for TVP5146/47/46M2/47M1 power up
+ * Sequence.
+ * @ no_regs - Number of registers to write for power up sequence.
+ * @ init_reg_seq - Array of registers and respective value to write.
+ */
+struct tvp514x_init_seq {
+ unsigned int no_regs;
+ const struct tvp514x_reg *init_reg_seq;
+};
+#endif /* ifndef _TVP514X_REGS_H */
diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c
index 28af5ce5560d..a388a9f0cb18 100644
--- a/drivers/media/video/tvp5150.c
+++ b/drivers/media/video/tvp5150.c
@@ -9,8 +9,10 @@
#include <linux/videodev2.h>
#include <linux/delay.h>
#include <linux/video_decoder.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/tvp5150.h>
+#include <media/v4l2-i2c-drv-legacy.h>
+#include <media/v4l2-chip-ident.h>
#include "tvp5150_reg.h"
@@ -29,21 +31,7 @@ I2C_CLIENT_INSMOD;
static int debug;
module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "Debug level (0-1)");
-
-#define tvp5150_err(fmt, arg...) do { \
- printk(KERN_ERR "%s %d-%04x: " fmt, c->driver->driver.name, \
- i2c_adapter_id(c->adapter), c->addr , ## arg); } while (0)
-#define tvp5150_info(fmt, arg...) do { \
- printk(KERN_INFO "%s %d-%04x: " fmt, c->driver->driver.name, \
- i2c_adapter_id(c->adapter), c->addr , ## arg); } while (0)
-#define tvp5150_dbg(num, fmt, arg...) \
- do { \
- if (debug >= num) \
- printk(KERN_DEBUG "%s debug %d-%04x: " fmt,\
- c->driver->driver.name, \
- i2c_adapter_id(c->adapter), \
- c->addr , ## arg); } while (0)
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
/* supported controls */
static struct v4l2_queryctrl tvp5150_qctrl[] = {
@@ -87,7 +75,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = {
};
struct tvp5150 {
- struct i2c_client *client;
+ struct v4l2_subdev sd;
v4l2_std_id norm; /* Current set standard */
struct v4l2_routing route;
@@ -98,49 +86,57 @@ struct tvp5150 {
int sat;
};
-static int tvp5150_read(struct i2c_client *c, unsigned char addr)
+static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd)
{
+ return container_of(sd, struct tvp5150, sd);
+}
+
+static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer[1];
int rc;
buffer[0] = addr;
if (1 != (rc = i2c_master_send(c, buffer, 1)))
- tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 1)\n", rc);
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
msleep(10);
if (1 != (rc = i2c_master_recv(c, buffer, 1)))
- tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 1)\n", rc);
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
- tvp5150_dbg(2, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]);
+ v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]);
return (buffer[0]);
}
-static inline void tvp5150_write(struct i2c_client *c, unsigned char addr,
+static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr,
unsigned char value)
{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
int rc;
buffer[0] = addr;
buffer[1] = value;
- tvp5150_dbg(2, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]);
+ v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]);
if (2 != (rc = i2c_master_send(c, buffer, 2)))
- tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 2)\n", rc);
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc);
}
-static void dump_reg_range(struct i2c_client *c, char *s, u8 init, const u8 end,int max_line)
+static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init,
+ const u8 end, int max_line)
{
- int i=0;
+ int i = 0;
- while (init!=(u8)(end+1)) {
- if ((i%max_line) == 0) {
- if (i>0)
+ while (init != (u8)(end + 1)) {
+ if ((i % max_line) == 0) {
+ if (i > 0)
printk("\n");
- printk("tvp5150: %s reg 0x%02x = ",s,init);
+ printk("tvp5150: %s reg 0x%02x = ", s, init);
}
- printk("%02x ",tvp5150_read(c, init));
+ printk("%02x ", tvp5150_read(sd, init));
init++;
i++;
@@ -148,147 +144,148 @@ static void dump_reg_range(struct i2c_client *c, char *s, u8 init, const u8 end,
printk("\n");
}
-static void dump_reg(struct i2c_client *c)
+static int tvp5150_log_status(struct v4l2_subdev *sd)
{
printk("tvp5150: Video input source selection #1 = 0x%02x\n",
- tvp5150_read(c, TVP5150_VD_IN_SRC_SEL_1));
+ tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1));
printk("tvp5150: Analog channel controls = 0x%02x\n",
- tvp5150_read(c, TVP5150_ANAL_CHL_CTL));
+ tvp5150_read(sd, TVP5150_ANAL_CHL_CTL));
printk("tvp5150: Operation mode controls = 0x%02x\n",
- tvp5150_read(c, TVP5150_OP_MODE_CTL));
+ tvp5150_read(sd, TVP5150_OP_MODE_CTL));
printk("tvp5150: Miscellaneous controls = 0x%02x\n",
- tvp5150_read(c, TVP5150_MISC_CTL));
+ tvp5150_read(sd, TVP5150_MISC_CTL));
printk("tvp5150: Autoswitch mask= 0x%02x\n",
- tvp5150_read(c, TVP5150_AUTOSW_MSK));
+ tvp5150_read(sd, TVP5150_AUTOSW_MSK));
printk("tvp5150: Color killer threshold control = 0x%02x\n",
- tvp5150_read(c, TVP5150_COLOR_KIL_THSH_CTL));
+ tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL));
printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n",
- tvp5150_read(c, TVP5150_LUMA_PROC_CTL_1),
- tvp5150_read(c, TVP5150_LUMA_PROC_CTL_2),
- tvp5150_read(c, TVP5150_LUMA_PROC_CTL_3));
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1),
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2),
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3));
printk("tvp5150: Brightness control = 0x%02x\n",
- tvp5150_read(c, TVP5150_BRIGHT_CTL));
+ tvp5150_read(sd, TVP5150_BRIGHT_CTL));
printk("tvp5150: Color saturation control = 0x%02x\n",
- tvp5150_read(c, TVP5150_SATURATION_CTL));
+ tvp5150_read(sd, TVP5150_SATURATION_CTL));
printk("tvp5150: Hue control = 0x%02x\n",
- tvp5150_read(c, TVP5150_HUE_CTL));
+ tvp5150_read(sd, TVP5150_HUE_CTL));
printk("tvp5150: Contrast control = 0x%02x\n",
- tvp5150_read(c, TVP5150_CONTRAST_CTL));
+ tvp5150_read(sd, TVP5150_CONTRAST_CTL));
printk("tvp5150: Outputs and data rates select = 0x%02x\n",
- tvp5150_read(c, TVP5150_DATA_RATE_SEL));
+ tvp5150_read(sd, TVP5150_DATA_RATE_SEL));
printk("tvp5150: Configuration shared pins = 0x%02x\n",
- tvp5150_read(c, TVP5150_CONF_SHARED_PIN));
+ tvp5150_read(sd, TVP5150_CONF_SHARED_PIN));
printk("tvp5150: Active video cropping start = 0x%02x%02x\n",
- tvp5150_read(c, TVP5150_ACT_VD_CROP_ST_MSB),
- tvp5150_read(c, TVP5150_ACT_VD_CROP_ST_LSB));
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB),
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB));
printk("tvp5150: Active video cropping stop = 0x%02x%02x\n",
- tvp5150_read(c, TVP5150_ACT_VD_CROP_STP_MSB),
- tvp5150_read(c, TVP5150_ACT_VD_CROP_STP_LSB));
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB),
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB));
printk("tvp5150: Genlock/RTC = 0x%02x\n",
- tvp5150_read(c, TVP5150_GENLOCK));
+ tvp5150_read(sd, TVP5150_GENLOCK));
printk("tvp5150: Horizontal sync start = 0x%02x\n",
- tvp5150_read(c, TVP5150_HORIZ_SYNC_START));
+ tvp5150_read(sd, TVP5150_HORIZ_SYNC_START));
printk("tvp5150: Vertical blanking start = 0x%02x\n",
- tvp5150_read(c, TVP5150_VERT_BLANKING_START));
+ tvp5150_read(sd, TVP5150_VERT_BLANKING_START));
printk("tvp5150: Vertical blanking stop = 0x%02x\n",
- tvp5150_read(c, TVP5150_VERT_BLANKING_STOP));
+ tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP));
printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n",
- tvp5150_read(c, TVP5150_CHROMA_PROC_CTL_1),
- tvp5150_read(c, TVP5150_CHROMA_PROC_CTL_2));
+ tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1),
+ tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2));
printk("tvp5150: Interrupt reset register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_RESET_REG_B));
+ tvp5150_read(sd, TVP5150_INT_RESET_REG_B));
printk("tvp5150: Interrupt enable register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_ENABLE_REG_B));
+ tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B));
printk("tvp5150: Interrupt configuration register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INTT_CONFIG_REG_B));
+ tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B));
printk("tvp5150: Video standard = 0x%02x\n",
- tvp5150_read(c, TVP5150_VIDEO_STD));
+ tvp5150_read(sd, TVP5150_VIDEO_STD));
printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n",
- tvp5150_read(c, TVP5150_CB_GAIN_FACT),
- tvp5150_read(c, TVP5150_CR_GAIN_FACTOR));
+ tvp5150_read(sd, TVP5150_CB_GAIN_FACT),
+ tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR));
printk("tvp5150: Macrovision on counter = 0x%02x\n",
- tvp5150_read(c, TVP5150_MACROVISION_ON_CTR));
+ tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR));
printk("tvp5150: Macrovision off counter = 0x%02x\n",
- tvp5150_read(c, TVP5150_MACROVISION_OFF_CTR));
+ tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR));
printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n",
- (tvp5150_read(c, TVP5150_REV_SELECT)&1)?3:4);
+ (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4);
printk("tvp5150: Device ID = %02x%02x\n",
- tvp5150_read(c, TVP5150_MSB_DEV_ID),
- tvp5150_read(c, TVP5150_LSB_DEV_ID));
+ tvp5150_read(sd, TVP5150_MSB_DEV_ID),
+ tvp5150_read(sd, TVP5150_LSB_DEV_ID));
printk("tvp5150: ROM version = (hex) %02x.%02x\n",
- tvp5150_read(c, TVP5150_ROM_MAJOR_VER),
- tvp5150_read(c, TVP5150_ROM_MINOR_VER));
+ tvp5150_read(sd, TVP5150_ROM_MAJOR_VER),
+ tvp5150_read(sd, TVP5150_ROM_MINOR_VER));
printk("tvp5150: Vertical line count = 0x%02x%02x\n",
- tvp5150_read(c, TVP5150_VERT_LN_COUNT_MSB),
- tvp5150_read(c, TVP5150_VERT_LN_COUNT_LSB));
+ tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB),
+ tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB));
printk("tvp5150: Interrupt status register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_STATUS_REG_B));
+ tvp5150_read(sd, TVP5150_INT_STATUS_REG_B));
printk("tvp5150: Interrupt active register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_ACTIVE_REG_B));
+ tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B));
printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n",
- tvp5150_read(c, TVP5150_STATUS_REG_1),
- tvp5150_read(c, TVP5150_STATUS_REG_2),
- tvp5150_read(c, TVP5150_STATUS_REG_3),
- tvp5150_read(c, TVP5150_STATUS_REG_4),
- tvp5150_read(c, TVP5150_STATUS_REG_5));
+ tvp5150_read(sd, TVP5150_STATUS_REG_1),
+ tvp5150_read(sd, TVP5150_STATUS_REG_2),
+ tvp5150_read(sd, TVP5150_STATUS_REG_3),
+ tvp5150_read(sd, TVP5150_STATUS_REG_4),
+ tvp5150_read(sd, TVP5150_STATUS_REG_5));
- dump_reg_range(c,"Teletext filter 1", TVP5150_TELETEXT_FIL1_INI,
- TVP5150_TELETEXT_FIL1_END,8);
- dump_reg_range(c,"Teletext filter 2", TVP5150_TELETEXT_FIL2_INI,
- TVP5150_TELETEXT_FIL2_END,8);
+ dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI,
+ TVP5150_TELETEXT_FIL1_END, 8);
+ dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI,
+ TVP5150_TELETEXT_FIL2_END, 8);
printk("tvp5150: Teletext filter enable = 0x%02x\n",
- tvp5150_read(c, TVP5150_TELETEXT_FIL_ENA));
+ tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA));
printk("tvp5150: Interrupt status register A = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_STATUS_REG_A));
+ tvp5150_read(sd, TVP5150_INT_STATUS_REG_A));
printk("tvp5150: Interrupt enable register A = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_ENABLE_REG_A));
+ tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A));
printk("tvp5150: Interrupt configuration = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_CONF));
+ tvp5150_read(sd, TVP5150_INT_CONF));
printk("tvp5150: VDP status register = 0x%02x\n",
- tvp5150_read(c, TVP5150_VDP_STATUS_REG));
+ tvp5150_read(sd, TVP5150_VDP_STATUS_REG));
printk("tvp5150: FIFO word count = 0x%02x\n",
- tvp5150_read(c, TVP5150_FIFO_WORD_COUNT));
+ tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT));
printk("tvp5150: FIFO interrupt threshold = 0x%02x\n",
- tvp5150_read(c, TVP5150_FIFO_INT_THRESHOLD));
+ tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD));
printk("tvp5150: FIFO reset = 0x%02x\n",
- tvp5150_read(c, TVP5150_FIFO_RESET));
+ tvp5150_read(sd, TVP5150_FIFO_RESET));
printk("tvp5150: Line number interrupt = 0x%02x\n",
- tvp5150_read(c, TVP5150_LINE_NUMBER_INT));
+ tvp5150_read(sd, TVP5150_LINE_NUMBER_INT));
printk("tvp5150: Pixel alignment register = 0x%02x%02x\n",
- tvp5150_read(c, TVP5150_PIX_ALIGN_REG_HIGH),
- tvp5150_read(c, TVP5150_PIX_ALIGN_REG_LOW));
+ tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH),
+ tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW));
printk("tvp5150: FIFO output control = 0x%02x\n",
- tvp5150_read(c, TVP5150_FIFO_OUT_CTRL));
+ tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL));
printk("tvp5150: Full field enable = 0x%02x\n",
- tvp5150_read(c, TVP5150_FULL_FIELD_ENA));
+ tvp5150_read(sd, TVP5150_FULL_FIELD_ENA));
printk("tvp5150: Full field mode register = 0x%02x\n",
- tvp5150_read(c, TVP5150_FULL_FIELD_MODE_REG));
+ tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG));
- dump_reg_range(c,"CC data", TVP5150_CC_DATA_INI,
- TVP5150_CC_DATA_END,8);
+ dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI,
+ TVP5150_CC_DATA_END, 8);
- dump_reg_range(c,"WSS data", TVP5150_WSS_DATA_INI,
- TVP5150_WSS_DATA_END,8);
+ dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI,
+ TVP5150_WSS_DATA_END, 8);
- dump_reg_range(c,"VPS data", TVP5150_VPS_DATA_INI,
- TVP5150_VPS_DATA_END,8);
+ dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI,
+ TVP5150_VPS_DATA_END, 8);
- dump_reg_range(c,"VITC data", TVP5150_VITC_DATA_INI,
- TVP5150_VITC_DATA_END,10);
+ dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI,
+ TVP5150_VITC_DATA_END, 10);
- dump_reg_range(c,"Line mode", TVP5150_LINE_MODE_INI,
- TVP5150_LINE_MODE_END,8);
+ dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI,
+ TVP5150_LINE_MODE_END, 8);
+ return 0;
}
/****************************************************************************
Basic functions
****************************************************************************/
-static inline void tvp5150_selmux(struct i2c_client *c)
+static inline void tvp5150_selmux(struct v4l2_subdev *sd)
{
int opmode=0;
- struct tvp5150 *decoder = i2c_get_clientdata(c);
+ struct tvp5150 *decoder = to_tvp5150(sd);
int input = 0;
unsigned char val;
@@ -309,23 +306,23 @@ static inline void tvp5150_selmux(struct i2c_client *c)
break;
}
- tvp5150_dbg( 1, "Selecting video route: route input=%i, output=%i "
+ v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i "
"=> tvp5150 input=%i, opmode=%i\n",
decoder->route.input,decoder->route.output,
input, opmode );
- tvp5150_write(c, TVP5150_OP_MODE_CTL, opmode);
- tvp5150_write(c, TVP5150_VD_IN_SRC_SEL_1, input);
+ tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode);
+ tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input);
/* Svideo should enable YCrCb output and disable GPCL output
* For Composite and TV, it should be the reverse
*/
- val = tvp5150_read(c, TVP5150_MISC_CTL);
+ val = tvp5150_read(sd, TVP5150_MISC_CTL);
if (decoder->route.input == TVP5150_SVIDEO)
val = (val & ~0x40) | 0x10;
else
val = (val & ~0x10) | 0x40;
- tvp5150_write(c, TVP5150_MISC_CTL, val);
+ tvp5150_write(sd, TVP5150_MISC_CTL, val);
};
struct i2c_reg_value {
@@ -593,35 +590,35 @@ static struct i2c_vbi_ram_value vbi_ram_default[] =
{ (u16)-1 }
};
-static int tvp5150_write_inittab(struct i2c_client *c,
+static int tvp5150_write_inittab(struct v4l2_subdev *sd,
const struct i2c_reg_value *regs)
{
while (regs->reg != 0xff) {
- tvp5150_write(c, regs->reg, regs->value);
+ tvp5150_write(sd, regs->reg, regs->value);
regs++;
}
return 0;
}
-static int tvp5150_vdp_init(struct i2c_client *c,
+static int tvp5150_vdp_init(struct v4l2_subdev *sd,
const struct i2c_vbi_ram_value *regs)
{
unsigned int i;
/* Disable Full Field */
- tvp5150_write(c, TVP5150_FULL_FIELD_ENA, 0);
+ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
/* Before programming, Line mode should be at 0xff */
- for (i=TVP5150_LINE_MODE_INI; i<=TVP5150_LINE_MODE_END; i++)
- tvp5150_write(c, i, 0xff);
+ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
+ tvp5150_write(sd, i, 0xff);
/* Load Ram Table */
- while (regs->reg != (u16)-1 ) {
- tvp5150_write(c, TVP5150_CONF_RAM_ADDR_HIGH,regs->reg>>8);
- tvp5150_write(c, TVP5150_CONF_RAM_ADDR_LOW,regs->reg);
+ while (regs->reg != (u16)-1) {
+ tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8);
+ tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg);
- for (i=0;i<16;i++)
- tvp5150_write(c, TVP5150_VDP_CONF_RAM_DATA,regs->values[i]);
+ for (i = 0; i < 16; i++)
+ tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]);
regs++;
}
@@ -629,11 +626,13 @@ static int tvp5150_vdp_init(struct i2c_client *c,
}
/* Fills VBI capabilities based on i2c_vbi_ram_value struct */
-static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs,
+static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd,
struct v4l2_sliced_vbi_cap *cap)
{
+ const struct i2c_vbi_ram_value *regs = vbi_ram_default;
int line;
+ v4l2_dbg(1, debug, sd, "VIDIOC_G_SLICED_VBI_CAP\n");
memset(cap, 0, sizeof *cap);
while (regs->reg != (u16)-1 ) {
@@ -644,6 +643,7 @@ static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs,
regs++;
}
+ return 0;
}
/* Set vbi processing
@@ -659,18 +659,18 @@ static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs,
* LSB = field1
* MSB = field2
*/
-static int tvp5150_set_vbi(struct i2c_client *c,
+static int tvp5150_set_vbi(struct v4l2_subdev *sd,
const struct i2c_vbi_ram_value *regs,
unsigned int type,u8 flags, int line,
const int fields)
{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- v4l2_std_id std=decoder->norm;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std = decoder->norm;
u8 reg;
int pos=0;
if (std == V4L2_STD_ALL) {
- tvp5150_err("VBI can't be configured without knowing number of lines\n");
+ v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
return 0;
} else if (std & V4L2_STD_625_50) {
/* Don't follow NTSC Line number convension */
@@ -698,163 +698,186 @@ static int tvp5150_set_vbi(struct i2c_client *c,
reg=((line-6)<<1)+TVP5150_LINE_MODE_INI;
if (fields&1) {
- tvp5150_write(c, reg, type);
+ tvp5150_write(sd, reg, type);
}
if (fields&2) {
- tvp5150_write(c, reg+1, type);
+ tvp5150_write(sd, reg+1, type);
}
return type;
}
-static int tvp5150_get_vbi(struct i2c_client *c,
+static int tvp5150_get_vbi(struct v4l2_subdev *sd,
const struct i2c_vbi_ram_value *regs, int line)
{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- v4l2_std_id std=decoder->norm;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std = decoder->norm;
u8 reg;
- int pos, type=0;
+ int pos, type = 0;
if (std == V4L2_STD_ALL) {
- tvp5150_err("VBI can't be configured without knowing number of lines\n");
+ v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
return 0;
} else if (std & V4L2_STD_625_50) {
/* Don't follow NTSC Line number convension */
line += 3;
}
- if (line<6||line>27)
+ if (line < 6 || line > 27)
return 0;
- reg=((line-6)<<1)+TVP5150_LINE_MODE_INI;
+ reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
- pos=tvp5150_read(c, reg)&0x0f;
- if (pos<0x0f)
- type=regs[pos].type.vbi_type;
+ pos = tvp5150_read(sd, reg) & 0x0f;
+ if (pos < 0x0f)
+ type = regs[pos].type.vbi_type;
- pos=tvp5150_read(c, reg+1)&0x0f;
- if (pos<0x0f)
- type|=regs[pos].type.vbi_type;
+ pos = tvp5150_read(sd, reg + 1) & 0x0f;
+ if (pos < 0x0f)
+ type |= regs[pos].type.vbi_type;
return type;
}
-static int tvp5150_set_std(struct i2c_client *c, v4l2_std_id std)
+
+static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- int fmt=0;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ int fmt = 0;
- decoder->norm=std;
+ decoder->norm = std;
/* First tests should be against specific std */
if (std == V4L2_STD_ALL) {
- fmt=0; /* Autodetect mode */
+ fmt = 0; /* Autodetect mode */
} else if (std & V4L2_STD_NTSC_443) {
- fmt=0xa;
+ fmt = 0xa;
} else if (std & V4L2_STD_PAL_M) {
- fmt=0x6;
- } else if (std & (V4L2_STD_PAL_N| V4L2_STD_PAL_Nc)) {
- fmt=0x8;
+ fmt = 0x6;
+ } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
+ fmt = 0x8;
} else {
/* Then, test against generic ones */
- if (std & V4L2_STD_NTSC) {
- fmt=0x2;
- } else if (std & V4L2_STD_PAL) {
- fmt=0x4;
- } else if (std & V4L2_STD_SECAM) {
- fmt=0xc;
- }
+ if (std & V4L2_STD_NTSC)
+ fmt = 0x2;
+ else if (std & V4L2_STD_PAL)
+ fmt = 0x4;
+ else if (std & V4L2_STD_SECAM)
+ fmt = 0xc;
}
- tvp5150_dbg(1,"Set video std register to %d.\n",fmt);
- tvp5150_write(c, TVP5150_VIDEO_STD, fmt);
-
+ v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt);
+ tvp5150_write(sd, TVP5150_VIDEO_STD, fmt);
return 0;
}
-static inline void tvp5150_reset(struct i2c_client *c)
+static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ if (decoder->norm == std)
+ return 0;
+
+ return tvp5150_set_std(sd, std);
+}
+
+static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
{
+ struct tvp5150 *decoder = to_tvp5150(sd);
u8 msb_id, lsb_id, msb_rom, lsb_rom;
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- msb_id=tvp5150_read(c,TVP5150_MSB_DEV_ID);
- lsb_id=tvp5150_read(c,TVP5150_LSB_DEV_ID);
- msb_rom=tvp5150_read(c,TVP5150_ROM_MAJOR_VER);
- lsb_rom=tvp5150_read(c,TVP5150_ROM_MINOR_VER);
+ msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID);
+ lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID);
+ msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER);
+ lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
- if ((msb_rom==4)&&(lsb_rom==0)) { /* Is TVP5150AM1 */
- tvp5150_info("tvp%02x%02xam1 detected.\n",msb_id, lsb_id);
+ if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */
+ v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id);
/* ITU-T BT.656.4 timing */
- tvp5150_write(c,TVP5150_REV_SELECT,0);
+ tvp5150_write(sd, TVP5150_REV_SELECT, 0);
} else {
- if ((msb_rom==3)||(lsb_rom==0x21)) { /* Is TVP5150A */
- tvp5150_info("tvp%02x%02xa detected.\n",msb_id, lsb_id);
+ if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */
+ v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id);
} else {
- tvp5150_info("*** unknown tvp%02x%02x chip detected.\n",msb_id,lsb_id);
- tvp5150_info("*** Rom ver is %d.%d\n",msb_rom,lsb_rom);
+ v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n",
+ msb_id, lsb_id);
+ v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom);
}
}
/* Initializes TVP5150 to its default values */
- tvp5150_write_inittab(c, tvp5150_init_default);
+ tvp5150_write_inittab(sd, tvp5150_init_default);
/* Initializes VDP registers */
- tvp5150_vdp_init(c, vbi_ram_default);
+ tvp5150_vdp_init(sd, vbi_ram_default);
/* Selects decoder input */
- tvp5150_selmux(c);
+ tvp5150_selmux(sd);
/* Initializes TVP5150 to stream enabled values */
- tvp5150_write_inittab(c, tvp5150_init_enable);
+ tvp5150_write_inittab(sd, tvp5150_init_enable);
/* Initialize image preferences */
- tvp5150_write(c, TVP5150_BRIGHT_CTL, decoder->bright);
- tvp5150_write(c, TVP5150_CONTRAST_CTL, decoder->contrast);
- tvp5150_write(c, TVP5150_SATURATION_CTL, decoder->contrast);
- tvp5150_write(c, TVP5150_HUE_CTL, decoder->hue);
+ tvp5150_write(sd, TVP5150_BRIGHT_CTL, decoder->bright);
+ tvp5150_write(sd, TVP5150_CONTRAST_CTL, decoder->contrast);
+ tvp5150_write(sd, TVP5150_SATURATION_CTL, decoder->contrast);
+ tvp5150_write(sd, TVP5150_HUE_CTL, decoder->hue);
- tvp5150_set_std(c, decoder->norm);
+ tvp5150_set_std(sd, decoder->norm);
+ return 0;
};
-static int tvp5150_get_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
+static int tvp5150_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
-/* struct tvp5150 *decoder = i2c_get_clientdata(c); */
+ v4l2_dbg(1, debug, sd, "VIDIOC_G_CTRL called\n");
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- ctrl->value = tvp5150_read(c, TVP5150_BRIGHT_CTL);
+ ctrl->value = tvp5150_read(sd, TVP5150_BRIGHT_CTL);
return 0;
case V4L2_CID_CONTRAST:
- ctrl->value = tvp5150_read(c, TVP5150_CONTRAST_CTL);
+ ctrl->value = tvp5150_read(sd, TVP5150_CONTRAST_CTL);
return 0;
case V4L2_CID_SATURATION:
- ctrl->value = tvp5150_read(c, TVP5150_SATURATION_CTL);
+ ctrl->value = tvp5150_read(sd, TVP5150_SATURATION_CTL);
return 0;
case V4L2_CID_HUE:
- ctrl->value = tvp5150_read(c, TVP5150_HUE_CTL);
+ ctrl->value = tvp5150_read(sd, TVP5150_HUE_CTL);
return 0;
}
return -EINVAL;
}
-static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
+static int tvp5150_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
-/* struct tvp5150 *decoder = i2c_get_clientdata(c); */
+ u8 i, n;
+ n = ARRAY_SIZE(tvp5150_qctrl);
+
+ for (i = 0; i < n; i++) {
+ if (ctrl->id != tvp5150_qctrl[i].id)
+ continue;
+ if (ctrl->value < tvp5150_qctrl[i].minimum ||
+ ctrl->value > tvp5150_qctrl[i].maximum)
+ return -ERANGE;
+ v4l2_dbg(1, debug, sd, "VIDIOC_S_CTRL: id=%d, value=%d\n",
+ ctrl->id, ctrl->value);
+ break;
+ }
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- tvp5150_write(c, TVP5150_BRIGHT_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->value);
return 0;
case V4L2_CID_CONTRAST:
- tvp5150_write(c, TVP5150_CONTRAST_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->value);
return 0;
case V4L2_CID_SATURATION:
- tvp5150_write(c, TVP5150_SATURATION_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->value);
return 0;
case V4L2_CID_HUE:
- tvp5150_write(c, TVP5150_HUE_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->value);
return 0;
}
return -EINVAL;
@@ -863,227 +886,210 @@ static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
/****************************************************************************
I2C Command
****************************************************************************/
-static int tvp5150_command(struct i2c_client *c,
- unsigned int cmd, void *arg)
-{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- switch (cmd) {
+static int tvp5150_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
- case 0:
- case VIDIOC_INT_RESET:
- tvp5150_reset(c);
- break;
- case VIDIOC_INT_G_VIDEO_ROUTING:
- {
- struct v4l2_routing *route = arg;
+ decoder->route = *route;
+ tvp5150_selmux(sd);
+ return 0;
+}
- *route = decoder->route;
- break;
+static int tvp5150_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct v4l2_sliced_vbi_format *svbi;
+ int i;
+
+ /* raw vbi */
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ /* this is for capturing 36 raw vbi lines
+ if there's a way to cut off the beginning 2 vbi lines
+ with the tvp5150 then the vbi line count could be lowered
+ to 17 lines/field again, although I couldn't find a register
+ which could do that cropping */
+ if (fmt->fmt.vbi.sample_format == V4L2_PIX_FMT_GREY)
+ tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70);
+ if (fmt->fmt.vbi.count[0] == 18 && fmt->fmt.vbi.count[1] == 18) {
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00);
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01);
+ }
+ return 0;
}
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- struct v4l2_routing *route = arg;
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ svbi = &fmt->fmt.sliced;
+ if (svbi->service_set != 0) {
+ for (i = 0; i <= 23; i++) {
+ svbi->service_lines[1][i] = 0;
+ svbi->service_lines[0][i] =
+ tvp5150_set_vbi(sd, vbi_ram_default,
+ svbi->service_lines[0][i], 0xf0, i, 3);
+ }
+ /* Enables FIFO */
+ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1);
+ } else {
+ /* Disables FIFO*/
+ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0);
- decoder->route = *route;
- tvp5150_selmux(c);
- break;
+ /* Disable Full Field */
+ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
+
+ /* Disable Line modes */
+ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
+ tvp5150_write(sd, i, 0xff);
}
- case VIDIOC_S_STD:
- if (decoder->norm == *(v4l2_std_id *)arg)
- break;
- return tvp5150_set_std(c, *(v4l2_std_id *)arg);
- case VIDIOC_G_STD:
- *(v4l2_std_id *)arg = decoder->norm;
- break;
+ return 0;
+}
- case VIDIOC_G_SLICED_VBI_CAP:
- {
- struct v4l2_sliced_vbi_cap *cap = arg;
- tvp5150_dbg(1, "VIDIOC_G_SLICED_VBI_CAP\n");
+static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct v4l2_sliced_vbi_format *svbi;
+ int i, mask = 0;
- tvp5150_vbi_get_cap(vbi_ram_default, cap);
- break;
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ svbi = &fmt->fmt.sliced;
+ memset(svbi, 0, sizeof(*svbi));
+
+ for (i = 0; i <= 23; i++) {
+ svbi->service_lines[0][i] =
+ tvp5150_get_vbi(sd, vbi_ram_default, i);
+ mask |= svbi->service_lines[0][i];
}
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *fmt;
- struct v4l2_sliced_vbi_format *svbi;
- int i;
-
- fmt = arg;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
- svbi = &fmt->fmt.sliced;
- if (svbi->service_set != 0) {
- for (i = 0; i <= 23; i++) {
- svbi->service_lines[1][i] = 0;
-
- svbi->service_lines[0][i]=tvp5150_set_vbi(c,
- vbi_ram_default,
- svbi->service_lines[0][i],0xf0,i,3);
- }
- /* Enables FIFO */
- tvp5150_write(c, TVP5150_FIFO_OUT_CTRL,1);
- } else {
- /* Disables FIFO*/
- tvp5150_write(c, TVP5150_FIFO_OUT_CTRL,0);
+ svbi->service_set = mask;
+ return 0;
+}
- /* Disable Full Field */
- tvp5150_write(c, TVP5150_FULL_FIELD_ENA, 0);
- /* Disable Line modes */
- for (i=TVP5150_LINE_MODE_INI; i<=TVP5150_LINE_MODE_END; i++)
- tvp5150_write(c, i, 0xff);
- }
- break;
- }
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *fmt;
- struct v4l2_sliced_vbi_format *svbi;
+static int tvp5150_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_chip_ident *chip)
+{
+ int rev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- int i, mask=0;
+ rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 |
+ tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
- fmt = arg;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
- svbi = &fmt->fmt.sliced;
- memset(svbi, 0, sizeof(*svbi));
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150,
+ rev);
+}
- for (i = 0; i <= 23; i++) {
- svbi->service_lines[0][i]=tvp5150_get_vbi(c,
- vbi_ram_default,i);
- mask|=svbi->service_lines[0][i];
- }
- svbi->service_set=mask;
- break;
- }
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(c, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = tvp5150_read(c, reg->reg & 0xff);
- else
- tvp5150_write(c, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
-#endif
+static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_LOG_STATUS:
- dump_reg(c);
- break;
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = tvp5150_read(sd, reg->reg & 0xff);
+ return 0;
+}
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = arg;
- int status = tvp5150_read(c, 0x88);
+static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
- break;
- }
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
- int i;
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
- tvp5150_dbg(1, "VIDIOC_QUERYCTRL called\n");
+static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ int status = tvp5150_read(sd, 0x88);
- for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++)
- if (qc->id && qc->id == tvp5150_qctrl[i].id) {
- memcpy(qc, &(tvp5150_qctrl[i]),
- sizeof(*qc));
- return 0;
- }
+ vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
+ return 0;
+}
- return -EINVAL;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- tvp5150_dbg(1, "VIDIOC_G_CTRL called\n");
+static int tvp5150_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ int i;
- return tvp5150_get_ctrl(c, ctrl);
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- u8 i, n;
- n = ARRAY_SIZE(tvp5150_qctrl);
- for (i = 0; i < n; i++)
- if (ctrl->id == tvp5150_qctrl[i].id) {
- if (ctrl->value <
- tvp5150_qctrl[i].minimum
- || ctrl->value >
- tvp5150_qctrl[i].maximum)
- return -ERANGE;
- tvp5150_dbg(1,
- "VIDIOC_S_CTRL: id=%d, value=%d\n",
- ctrl->id, ctrl->value);
- return tvp5150_set_ctrl(c, ctrl);
- }
- return -EINVAL;
+ v4l2_dbg(1, debug, sd, "VIDIOC_QUERYCTRL called\n");
+
+ for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++)
+ if (qc->id && qc->id == tvp5150_qctrl[i].id) {
+ memcpy(qc, &(tvp5150_qctrl[i]),
+ sizeof(*qc));
+ return 0;
}
- default:
- return -EINVAL;
- }
+ return -EINVAL;
+}
- return 0;
+static int tvp5150_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
}
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tvp5150_core_ops = {
+ .log_status = tvp5150_log_status,
+ .g_ctrl = tvp5150_g_ctrl,
+ .s_ctrl = tvp5150_s_ctrl,
+ .queryctrl = tvp5150_queryctrl,
+ .reset = tvp5150_reset,
+ .g_chip_ident = tvp5150_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = tvp5150_g_register,
+ .s_register = tvp5150_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
+ .s_std = tvp5150_s_std,
+ .g_tuner = tvp5150_g_tuner,
+};
+
+static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
+ .s_routing = tvp5150_s_routing,
+ .g_fmt = tvp5150_g_fmt,
+ .s_fmt = tvp5150_s_fmt,
+ .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap,
+};
+
+static const struct v4l2_subdev_ops tvp5150_ops = {
+ .core = &tvp5150_core_ops,
+ .tuner = &tvp5150_tuner_ops,
+ .video = &tvp5150_video_ops,
+};
+
+
/****************************************************************************
I2C Client & Driver
****************************************************************************/
-static struct i2c_driver driver;
-
-static struct i2c_client client_template = {
- .name = "(unset)",
- .driver = &driver,
-};
-static int tvp5150_detect_client(struct i2c_adapter *adapter,
- int address, int kind)
+static int tvp5150_probe(struct i2c_client *c,
+ const struct i2c_device_id *id)
{
- struct i2c_client *c;
struct tvp5150 *core;
- int rv;
-
- if (debug)
- printk( KERN_INFO
- "tvp5150.c: detecting tvp5150 client on address 0x%x\n",
- address << 1);
-
- client_template.adapter = adapter;
- client_template.addr = address;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
- if (!i2c_check_functionality
- (adapter,
+ if (!i2c_check_functionality(c->adapter,
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
- return 0;
-
- c = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!c)
- return -ENOMEM;
- memcpy(c, &client_template, sizeof(struct i2c_client));
+ return -EIO;
core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL);
if (!core) {
- kfree(c);
return -ENOMEM;
}
- i2c_set_clientdata(c, core);
-
- rv = i2c_attach_client(c);
+ sd = &core->sd;
+ v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
+ v4l_info(c, "chip found @ 0x%02x (%s)\n",
+ c->addr << 1, c->adapter->name);
core->norm = V4L2_STD_ALL; /* Default is autodetect */
core->route.input = TVP5150_COMPOSITE1;
@@ -1093,69 +1099,38 @@ static int tvp5150_detect_client(struct i2c_adapter *adapter,
core->hue = 0;
core->sat = 128;
- if (rv) {
- kfree(c);
- kfree(core);
- return rv;
- }
-
if (debug > 1)
- dump_reg(c);
+ tvp5150_log_status(sd);
return 0;
}
-static int tvp5150_attach_adapter(struct i2c_adapter *adapter)
+static int tvp5150_remove(struct i2c_client *c)
{
- if (debug)
- printk( KERN_INFO
- "tvp5150.c: starting probe for adapter %s (0x%x)\n",
- adapter->name, adapter->id);
- return i2c_probe(adapter, &addr_data, &tvp5150_detect_client);
-}
-
-static int tvp5150_detach_client(struct i2c_client *c)
-{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- int err;
+ struct v4l2_subdev *sd = i2c_get_clientdata(c);
- tvp5150_dbg(1,
+ v4l2_dbg(1, debug, sd,
"tvp5150.c: removing tvp5150 adapter on address 0x%x\n",
c->addr << 1);
- err = i2c_detach_client(c);
- if (err) {
- return err;
- }
-
- kfree(decoder);
- kfree(c);
-
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_tvp5150(sd));
return 0;
}
/* ----------------------------------------------------------------------- */
-static struct i2c_driver driver = {
- .driver = {
- .name = "tvp5150",
- },
- .id = I2C_DRIVERID_TVP5150,
-
- .attach_adapter = tvp5150_attach_adapter,
- .detach_client = tvp5150_detach_client,
+static const struct i2c_device_id tvp5150_id[] = {
+ { "tvp5150", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tvp5150_id);
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tvp5150",
+ .driverid = I2C_DRIVERID_TVP5150,
.command = tvp5150_command,
+ .probe = tvp5150_probe,
+ .remove = tvp5150_remove,
+ .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
+ .id_table = tvp5150_id,
};
-
-static int __init tvp5150_init(void)
-{
- return i2c_add_driver(&driver);
-}
-
-static void __exit tvp5150_exit(void)
-{
- i2c_del_driver(&driver);
-}
-
-module_init(tvp5150_init);
-module_exit(tvp5150_exit);
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
new file mode 100644
index 000000000000..d5cdc4be1a35
--- /dev/null
+++ b/drivers/media/video/tw9910.c
@@ -0,0 +1,951 @@
+/*
+ * tw9910 Video Driver
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov772x driver,
+ *
+ * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/soc_camera.h>
+#include <media/tw9910.h>
+
+#define GET_ID(val) ((val & 0xF8) >> 3)
+#define GET_ReV(val) (val & 0x07)
+
+/*
+ * register offset
+ */
+#define ID 0x00 /* Product ID Code Register */
+#define STATUS1 0x01 /* Chip Status Register I */
+#define INFORM 0x02 /* Input Format */
+#define OPFORM 0x03 /* Output Format Control Register */
+#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */
+#define OUTCTR1 0x05 /* Output Control I */
+#define ACNTL1 0x06 /* Analog Control Register 1 */
+#define CROP_HI 0x07 /* Cropping Register, High */
+#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */
+#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */
+#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */
+#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */
+#define CNTRL1 0x0C /* Control Register I */
+#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */
+#define SCALE_HI 0x0E /* Scaling Register, High */
+#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */
+#define BRIGHT 0x10 /* BRIGHTNESS Control Register */
+#define CONTRAST 0x11 /* CONTRAST Control Register */
+#define SHARPNESS 0x12 /* SHARPNESS Control Register I */
+#define SAT_U 0x13 /* Chroma (U) Gain Register */
+#define SAT_V 0x14 /* Chroma (V) Gain Register */
+#define HUE 0x15 /* Hue Control Register */
+#define CORING1 0x17
+#define CORING2 0x18 /* Coring and IF compensation */
+#define VBICNTL 0x19 /* VBI Control Register */
+#define ACNTL2 0x1A /* Analog Control 2 */
+#define OUTCTR2 0x1B /* Output Control 2 */
+#define SDT 0x1C /* Standard Selection */
+#define SDTR 0x1D /* Standard Recognition */
+#define TEST 0x1F /* Test Control Register */
+#define CLMPG 0x20 /* Clamping Gain */
+#define IAGC 0x21 /* Individual AGC Gain */
+#define AGCGAIN 0x22 /* AGC Gain */
+#define PEAKWT 0x23 /* White Peak Threshold */
+#define CLMPL 0x24 /* Clamp level */
+#define SYNCT 0x25 /* Sync Amplitude */
+#define MISSCNT 0x26 /* Sync Miss Count Register */
+#define PCLAMP 0x27 /* Clamp Position Register */
+#define VCNTL1 0x28 /* Vertical Control I */
+#define VCNTL2 0x29 /* Vertical Control II */
+#define CKILL 0x2A /* Color Killer Level Control */
+#define COMB 0x2B /* Comb Filter Control */
+#define LDLY 0x2C /* Luma Delay and H Filter Control */
+#define MISC1 0x2D /* Miscellaneous Control I */
+#define LOOP 0x2E /* LOOP Control Register */
+#define MISC2 0x2F /* Miscellaneous Control II */
+#define MVSN 0x30 /* Macrovision Detection */
+#define STATUS2 0x31 /* Chip STATUS II */
+#define HFREF 0x32 /* H monitor */
+#define CLMD 0x33 /* CLAMP MODE */
+#define IDCNTL 0x34 /* ID Detection Control */
+#define CLCNTL1 0x35 /* Clamp Control I */
+#define ANAPLLCTL 0x4C
+#define VBIMIN 0x4D
+#define HSLOWCTL 0x4E
+#define WSS3 0x4F
+#define FILLDATA 0x50
+#define SDID 0x51
+#define DID 0x52
+#define WSS1 0x53
+#define WSS2 0x54
+#define VVBI 0x55
+#define LCTL6 0x56
+#define LCTL7 0x57
+#define LCTL8 0x58
+#define LCTL9 0x59
+#define LCTL10 0x5A
+#define LCTL11 0x5B
+#define LCTL12 0x5C
+#define LCTL13 0x5D
+#define LCTL14 0x5E
+#define LCTL15 0x5F
+#define LCTL16 0x60
+#define LCTL17 0x61
+#define LCTL18 0x62
+#define LCTL19 0x63
+#define LCTL20 0x64
+#define LCTL21 0x65
+#define LCTL22 0x66
+#define LCTL23 0x67
+#define LCTL24 0x68
+#define LCTL25 0x69
+#define LCTL26 0x6A
+#define HSGEGIN 0x6B
+#define HSEND 0x6C
+#define OVSDLY 0x6D
+#define OVSEND 0x6E
+#define VBIDELAY 0x6F
+
+/*
+ * register detail
+ */
+
+/* INFORM */
+#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */
+#define FC27_FF 0x00 /* 0 : Square pixel mode. */
+ /* Must use 24.54MHz for 60Hz field rate */
+ /* source or 29.5MHz for 50Hz field rate */
+#define IFSEL_S 0x10 /* 01 : S-video decoding */
+#define IFSEL_C 0x00 /* 00 : Composite video decoding */
+ /* Y input video selection */
+#define YSEL_M0 0x00 /* 00 : Mux0 selected */
+#define YSEL_M1 0x04 /* 01 : Mux1 selected */
+#define YSEL_M2 0x08 /* 10 : Mux2 selected */
+#define YSEL_M3 0x10 /* 11 : Mux3 selected */
+
+/* OPFORM */
+#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */
+ /* 1 : ITU-R-656 compatible data sequence format */
+#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */
+ /* 1 : 16-bit YCrCb 4:2:2 output format.*/
+#define LLCMODE 0x20 /* 1 : LLC output mode. */
+ /* 0 : free-run output mode */
+#define AINC 0x10 /* Serial interface auto-indexing control */
+ /* 0 : auto-increment */
+ /* 1 : non-auto */
+#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */
+ /* 0 : Vertical out ctrl by HACTIVE and DVALID */
+#define OEN 0x04 /* Output Enable together with TRI_SEL. */
+
+/* OUTCTR1 */
+#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */
+#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */
+ /* VS pin output control */
+#define VSSL_VSYNC 0x00 /* 0 : VSYNC */
+#define VSSL_VACT 0x10 /* 1 : VACT */
+#define VSSL_FIELD 0x20 /* 2 : FIELD */
+#define VSSL_VVALID 0x30 /* 3 : VVALID */
+#define VSSL_ZERO 0x70 /* 7 : 0 */
+#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */
+#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/
+ /* HS pin output control */
+#define HSSL_HACT 0x00 /* 0 : HACT */
+#define HSSL_HSYNC 0x01 /* 1 : HSYNC */
+#define HSSL_DVALID 0x02 /* 2 : DVALID */
+#define HSSL_HLOCK 0x03 /* 3 : HLOCK */
+#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */
+#define HSSL_ZERO 0x07 /* 7 : 0 */
+
+/* ACNTL1 */
+#define SRESET 0x80 /* resets the device to its default state
+ * but all register content remain unchanged.
+ * This bit is self-resetting.
+ */
+
+/* VBICNTL */
+/* RTSEL : control the real time signal
+* output from the MPOUT pin
+*/
+#define RTSEL_MASK 0x07
+#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */
+#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */
+#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */
+#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */
+#define RTSEL_MONO 0x04 /* 0100 = MONO */
+#define RTSEL_DET50 0x05 /* 0101 = DET50 */
+#define RTSEL_FIELD 0x06 /* 0110 = FIELD */
+#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */
+
+/*
+ * structure
+ */
+
+struct regval_list {
+ unsigned char reg_num;
+ unsigned char value;
+};
+
+struct tw9910_scale_ctrl {
+ char *name;
+ unsigned short width;
+ unsigned short height;
+ u16 hscale;
+ u16 vscale;
+};
+
+struct tw9910_cropping_ctrl {
+ u16 vdelay;
+ u16 vactive;
+ u16 hdelay;
+ u16 hactive;
+};
+
+struct tw9910_hsync_ctrl {
+ u16 start;
+ u16 end;
+};
+
+struct tw9910_priv {
+ struct tw9910_video_info *info;
+ struct i2c_client *client;
+ struct soc_camera_device icd;
+ const struct tw9910_scale_ctrl *scale;
+};
+
+/*
+ * register settings
+ */
+
+#define ENDMARKER { 0xff, 0xff }
+
+static const struct regval_list tw9910_default_regs[] =
+{
+ { OPFORM, 0x00 },
+ { OUTCTR1, VSP_LO | VSSL_VVALID | HSP_HI | HSSL_HSYNC },
+ ENDMARKER,
+};
+
+static const struct soc_camera_data_format tw9910_color_fmt[] = {
+ {
+ .name = "VYUY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ }
+};
+
+static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = {
+ {
+ .name = "NTSC SQ",
+ .width = 640,
+ .height = 480,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "NTSC CCIR601",
+ .width = 720,
+ .height = 480,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "NTSC SQ (CIF)",
+ .width = 320,
+ .height = 240,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "NTSC CCIR601 (CIF)",
+ .width = 360,
+ .height = 240,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "NTSC SQ (QCIF)",
+ .width = 160,
+ .height = 120,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+ {
+ .name = "NTSC CCIR601 (QCIF)",
+ .width = 180,
+ .height = 120,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+};
+
+static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
+ {
+ .name = "PAL SQ",
+ .width = 768,
+ .height = 576,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "PAL CCIR601",
+ .width = 720,
+ .height = 576,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "PAL SQ (CIF)",
+ .width = 384,
+ .height = 288,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "PAL CCIR601 (CIF)",
+ .width = 360,
+ .height = 288,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "PAL SQ (QCIF)",
+ .width = 192,
+ .height = 144,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+ {
+ .name = "PAL CCIR601 (QCIF)",
+ .width = 180,
+ .height = 144,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+};
+
+static const struct tw9910_cropping_ctrl tw9910_cropping_ctrl = {
+ .vdelay = 0x0012,
+ .vactive = 0x00F0,
+ .hdelay = 0x0010,
+ .hactive = 0x02D0,
+};
+
+static const struct tw9910_hsync_ctrl tw9910_hsync_ctrl = {
+ .start = 0x0260,
+ .end = 0x0300,
+};
+
+/*
+ * general function
+ */
+static int tw9910_set_scale(struct i2c_client *client,
+ const struct tw9910_scale_ctrl *scale)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, SCALE_HI,
+ (scale->vscale & 0x0F00) >> 4 |
+ (scale->hscale & 0x0F00) >> 8);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, HSCALE_LO,
+ scale->hscale & 0x00FF);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, VSCALE_LO,
+ scale->vscale & 0x00FF);
+
+ return ret;
+}
+
+static int tw9910_set_cropping(struct i2c_client *client,
+ const struct tw9910_cropping_ctrl *cropping)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, CROP_HI,
+ (cropping->vdelay & 0x0300) >> 2 |
+ (cropping->vactive & 0x0300) >> 4 |
+ (cropping->hdelay & 0x0300) >> 6 |
+ (cropping->hactive & 0x0300) >> 8);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, VDELAY_LO,
+ cropping->vdelay & 0x00FF);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, VACTIVE_LO,
+ cropping->vactive & 0x00FF);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, HDELAY_LO,
+ cropping->hdelay & 0x00FF);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, HACTIVE_LO,
+ cropping->hactive & 0x00FF);
+
+ return ret;
+}
+
+static int tw9910_set_hsync(struct i2c_client *client,
+ const struct tw9910_hsync_ctrl *hsync)
+{
+ int ret;
+
+ /* bit 10 - 3 */
+ ret = i2c_smbus_write_byte_data(client, HSGEGIN,
+ (hsync->start & 0x07F8) >> 3);
+ if (ret < 0)
+ return ret;
+
+ /* bit 10 - 3 */
+ ret = i2c_smbus_write_byte_data(client, HSEND,
+ (hsync->end & 0x07F8) >> 3);
+ if (ret < 0)
+ return ret;
+
+ /* bit 2 - 0 */
+ ret = i2c_smbus_read_byte_data(client, HSLOWCTL);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, HSLOWCTL,
+ (ret & 0x88) |
+ (hsync->start & 0x0007) << 4 |
+ (hsync->end & 0x0007));
+
+ return ret;
+}
+
+static int tw9910_write_array(struct i2c_client *client,
+ const struct regval_list *vals)
+{
+ while (vals->reg_num != 0xff) {
+ int ret = i2c_smbus_write_byte_data(client,
+ vals->reg_num,
+ vals->value);
+ if (ret < 0)
+ return ret;
+ vals++;
+ }
+ return 0;
+}
+
+static int tw9910_mask_set(struct i2c_client *client, u8 command,
+ u8 mask, u8 set)
+{
+ s32 val = i2c_smbus_read_byte_data(client, command);
+
+ val &= ~mask;
+ val |= set;
+
+ return i2c_smbus_write_byte_data(client, command, val);
+}
+
+static void tw9910_reset(struct i2c_client *client)
+{
+ i2c_smbus_write_byte_data(client, ACNTL1, SRESET);
+ msleep(1);
+}
+
+static const struct tw9910_scale_ctrl*
+tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height)
+{
+ const struct tw9910_scale_ctrl *scale;
+ const struct tw9910_scale_ctrl *ret = NULL;
+ v4l2_std_id norm = icd->vdev->current_norm;
+ __u32 diff = 0xffffffff, tmp;
+ int size, i;
+
+ if (norm & V4L2_STD_NTSC) {
+ scale = tw9910_ntsc_scales;
+ size = ARRAY_SIZE(tw9910_ntsc_scales);
+ } else if (norm & V4L2_STD_PAL) {
+ scale = tw9910_pal_scales;
+ size = ARRAY_SIZE(tw9910_pal_scales);
+ } else {
+ return NULL;
+ }
+
+ for (i = 0; i < size; i++) {
+ tmp = abs(width - scale[i].width) +
+ abs(height - scale[i].height);
+ if (tmp < diff) {
+ diff = tmp;
+ ret = scale + i;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * soc_camera_ops function
+ */
+static int tw9910_init(struct soc_camera_device *icd)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ int ret = 0;
+
+ if (priv->info->link.power) {
+ ret = priv->info->link.power(&priv->client->dev, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (priv->info->link.reset)
+ ret = priv->info->link.reset(&priv->client->dev);
+
+ return ret;
+}
+
+static int tw9910_release(struct soc_camera_device *icd)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ int ret = 0;
+
+ if (priv->info->link.power)
+ ret = priv->info->link.power(&priv->client->dev, 0);
+
+ return ret;
+}
+
+static int tw9910_start_capture(struct soc_camera_device *icd)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+
+ if (!priv->scale) {
+ dev_err(&icd->dev, "norm select error\n");
+ return -EPERM;
+ }
+
+ dev_dbg(&icd->dev, "%s %dx%d\n",
+ priv->scale->name,
+ priv->scale->width,
+ priv->scale->height);
+
+ return 0;
+}
+
+static int tw9910_stop_capture(struct soc_camera_device *icd)
+{
+ return 0;
+}
+
+static int tw9910_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+{
+ return 0;
+}
+
+static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ struct soc_camera_link *icl = priv->client->dev.platform_data;
+ unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
+ SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+static int tw9910_get_chip_id(struct soc_camera_device *icd,
+ struct v4l2_chip_ident *id)
+{
+ id->ident = V4L2_IDENT_TW9910;
+ id->revision = 0;
+
+ return 0;
+}
+
+static int tw9910_set_std(struct soc_camera_device *icd,
+ v4l2_std_id *a)
+{
+ int ret = -EINVAL;
+
+ if (*a & (V4L2_STD_NTSC | V4L2_STD_PAL))
+ ret = 0;
+
+ return ret;
+}
+
+static int tw9910_enum_input(struct soc_camera_device *icd,
+ struct v4l2_input *inp)
+{
+ inp->type = V4L2_INPUT_TYPE_TUNER;
+ inp->std = V4L2_STD_UNKNOWN;
+ strcpy(inp->name, "Video");
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int tw9910_get_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ int ret;
+
+ if (reg->reg > 0xff)
+ return -EINVAL;
+
+ ret = i2c_smbus_read_byte_data(priv->client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ /* ret = int
+ * reg->val = __u64
+ */
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int tw9910_set_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+
+ if (reg->reg > 0xff ||
+ reg->val > 0xff)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val);
+}
+#endif
+
+static int tw9910_set_fmt(struct soc_camera_device *icd, __u32 pixfmt,
+ struct v4l2_rect *rect)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ int ret = -EINVAL;
+ u8 val;
+
+ /*
+ * select suitable norm
+ */
+ priv->scale = tw9910_select_norm(icd, rect->width, rect->height);
+ if (!priv->scale)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * reset hardware
+ */
+ tw9910_reset(priv->client);
+ ret = tw9910_write_array(priv->client, tw9910_default_regs);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * set bus width
+ */
+ val = 0x00;
+ if (SOCAM_DATAWIDTH_16 == priv->info->buswidth)
+ val = LEN;
+
+ ret = tw9910_mask_set(priv->client, OPFORM, LEN, val);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * select MPOUT behavior
+ */
+ switch (priv->info->mpout) {
+ case TW9910_MPO_VLOSS:
+ val = RTSEL_VLOSS; break;
+ case TW9910_MPO_HLOCK:
+ val = RTSEL_HLOCK; break;
+ case TW9910_MPO_SLOCK:
+ val = RTSEL_SLOCK; break;
+ case TW9910_MPO_VLOCK:
+ val = RTSEL_VLOCK; break;
+ case TW9910_MPO_MONO:
+ val = RTSEL_MONO; break;
+ case TW9910_MPO_DET50:
+ val = RTSEL_DET50; break;
+ case TW9910_MPO_FIELD:
+ val = RTSEL_FIELD; break;
+ case TW9910_MPO_RTCO:
+ val = RTSEL_RTCO; break;
+ default:
+ val = 0;
+ }
+
+ ret = tw9910_mask_set(priv->client, VBICNTL, RTSEL_MASK, val);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * set scale
+ */
+ ret = tw9910_set_scale(priv->client, priv->scale);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * set cropping
+ */
+ ret = tw9910_set_cropping(priv->client, &tw9910_cropping_ctrl);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * set hsync
+ */
+ ret = tw9910_set_hsync(priv->client, &tw9910_hsync_ctrl);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ return ret;
+
+tw9910_set_fmt_error:
+
+ tw9910_reset(priv->client);
+ priv->scale = NULL;
+
+ return ret;
+}
+
+static int tw9910_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ const struct tw9910_scale_ctrl *scale;
+
+ if (V4L2_FIELD_ANY == pix->field) {
+ pix->field = V4L2_FIELD_INTERLACED;
+ } else if (V4L2_FIELD_INTERLACED != pix->field) {
+ dev_err(&icd->dev, "Field type invalid.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * select suitable norm
+ */
+ scale = tw9910_select_norm(icd, pix->width, pix->height);
+ if (!scale)
+ return -EINVAL;
+
+ pix->width = scale->width;
+ pix->height = scale->height;
+
+ return 0;
+}
+
+static int tw9910_video_probe(struct soc_camera_device *icd)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ s32 val;
+ int ret;
+
+ /*
+ * We must have a parent by now. And it cannot be a wrong one.
+ * So this entire test is completely redundant.
+ */
+ if (!icd->dev.parent ||
+ to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
+ return -ENODEV;
+
+ /*
+ * tw9910 only use 8 or 16 bit bus width
+ */
+ if (SOCAM_DATAWIDTH_16 != priv->info->buswidth &&
+ SOCAM_DATAWIDTH_8 != priv->info->buswidth) {
+ dev_err(&icd->dev, "bus width error\n");
+ return -ENODEV;
+ }
+
+ icd->formats = tw9910_color_fmt;
+ icd->num_formats = ARRAY_SIZE(tw9910_color_fmt);
+
+ /*
+ * check and show Product ID
+ */
+ val = i2c_smbus_read_byte_data(priv->client, ID);
+ if (0x0B != GET_ID(val) ||
+ 0x00 != GET_ReV(val)) {
+ dev_err(&icd->dev,
+ "Product ID error %x:%x\n", GET_ID(val), GET_ReV(val));
+ return -ENODEV;
+ }
+
+ dev_info(&icd->dev,
+ "tw9910 Product ID %0x:%0x\n", GET_ID(val), GET_ReV(val));
+
+ ret = soc_camera_video_start(icd);
+ if (ret < 0)
+ return ret;
+
+ icd->vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL;
+ icd->vdev->current_norm = V4L2_STD_NTSC;
+
+ return ret;
+}
+
+static void tw9910_video_remove(struct soc_camera_device *icd)
+{
+ soc_camera_video_stop(icd);
+}
+
+static struct soc_camera_ops tw9910_ops = {
+ .owner = THIS_MODULE,
+ .probe = tw9910_video_probe,
+ .remove = tw9910_video_remove,
+ .init = tw9910_init,
+ .release = tw9910_release,
+ .start_capture = tw9910_start_capture,
+ .stop_capture = tw9910_stop_capture,
+ .set_fmt = tw9910_set_fmt,
+ .try_fmt = tw9910_try_fmt,
+ .set_bus_param = tw9910_set_bus_param,
+ .query_bus_param = tw9910_query_bus_param,
+ .get_chip_id = tw9910_get_chip_id,
+ .set_std = tw9910_set_std,
+ .enum_input = tw9910_enum_input,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .get_register = tw9910_get_register,
+ .set_register = tw9910_set_register,
+#endif
+};
+
+/*
+ * i2c_driver function
+ */
+
+static int tw9910_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+
+{
+ struct tw9910_priv *priv;
+ struct tw9910_video_info *info;
+ struct soc_camera_device *icd;
+ const struct tw9910_scale_ctrl *scale;
+ int i, ret;
+
+ info = client->dev.platform_data;
+ if (!info)
+ return -EINVAL;
+
+ if (!i2c_check_functionality(to_i2c_adapter(client->dev.parent),
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev,
+ "I2C-Adapter doesn't support "
+ "I2C_FUNC_SMBUS_BYTE_DATA\n");
+ return -EIO;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = info;
+ priv->client = client;
+ i2c_set_clientdata(client, priv);
+
+ icd = &priv->icd;
+ icd->ops = &tw9910_ops;
+ icd->control = &client->dev;
+ icd->iface = info->link.bus_id;
+
+ /*
+ * set width and height
+ */
+ icd->width_max = tw9910_ntsc_scales[0].width; /* set default */
+ icd->width_min = tw9910_ntsc_scales[0].width;
+ icd->height_max = tw9910_ntsc_scales[0].height;
+ icd->height_min = tw9910_ntsc_scales[0].height;
+
+ scale = tw9910_ntsc_scales;
+ for (i = 0; i < ARRAY_SIZE(tw9910_ntsc_scales); i++) {
+ icd->width_max = max(scale[i].width, icd->width_max);
+ icd->width_min = min(scale[i].width, icd->width_min);
+ icd->height_max = max(scale[i].height, icd->height_max);
+ icd->height_min = min(scale[i].height, icd->height_min);
+ }
+ scale = tw9910_pal_scales;
+ for (i = 0; i < ARRAY_SIZE(tw9910_pal_scales); i++) {
+ icd->width_max = max(scale[i].width, icd->width_max);
+ icd->width_min = min(scale[i].width, icd->width_min);
+ icd->height_max = max(scale[i].height, icd->height_max);
+ icd->height_min = min(scale[i].height, icd->height_min);
+ }
+
+ ret = soc_camera_device_register(icd);
+
+ if (ret) {
+ i2c_set_clientdata(client, NULL);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int tw9910_remove(struct i2c_client *client)
+{
+ struct tw9910_priv *priv = i2c_get_clientdata(client);
+
+ soc_camera_device_unregister(&priv->icd);
+ i2c_set_clientdata(client, NULL);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id tw9910_id[] = {
+ { "tw9910", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tw9910_id);
+
+static struct i2c_driver tw9910_i2c_driver = {
+ .driver = {
+ .name = "tw9910",
+ },
+ .probe = tw9910_probe,
+ .remove = tw9910_remove,
+ .id_table = tw9910_id,
+};
+
+/*
+ * module function
+ */
+static int __init tw9910_module_init(void)
+{
+ return i2c_add_driver(&tw9910_i2c_driver);
+}
+
+static void __exit tw9910_module_exit(void)
+{
+ i2c_del_driver(&tw9910_i2c_driver);
+}
+
+module_init(tw9910_module_init);
+module_exit(tw9910_module_exit);
+
+MODULE_DESCRIPTION("SoC Camera driver for tw9910");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c
index b4628874933b..7a609a3a6dbe 100644
--- a/drivers/media/video/upd64031a.c
+++ b/drivers/media/video/upd64031a.c
@@ -26,7 +26,7 @@
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/upd64031a.h>
@@ -62,6 +62,7 @@ enum {
};
struct upd64031a_state {
+ struct v4l2_subdev sd;
u8 regs[TOT_REGS];
u8 gr_mode;
u8 direct_3dycs_connect;
@@ -69,6 +70,11 @@ struct upd64031a_state {
u8 ext_vert_sync;
};
+static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct upd64031a_state, sd);
+}
+
static u8 upd64031a_init[] = {
0x00, 0xb8, 0x48, 0xd2, 0xe6,
0x03, 0x10, 0x0b, 0xaf, 0x7f,
@@ -78,8 +84,9 @@ static u8 upd64031a_init[] = {
/* ------------------------------------------------------------------------ */
-static u8 upd64031a_read(struct i2c_client *client, u8 reg)
+static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 buf[2];
if (reg >= sizeof(buf))
@@ -90,106 +97,127 @@ static u8 upd64031a_read(struct i2c_client *client, u8 reg)
/* ------------------------------------------------------------------------ */
-static void upd64031a_write(struct i2c_client *client, u8 reg, u8 val)
+static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 buf[2];
buf[0] = reg;
buf[1] = val;
- v4l_dbg(1, debug, client, "write reg: %02X val: %02X\n", reg, val);
+ v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val);
if (i2c_master_send(client, buf, 2) != 2)
- v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val);
+ v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val);
}
/* ------------------------------------------------------------------------ */
/* The input changed due to new input or channel changed */
-static void upd64031a_change(struct i2c_client *client)
+static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
{
- struct upd64031a_state *state = i2c_get_clientdata(client);
+ struct upd64031a_state *state = to_state(sd);
u8 reg = state->regs[R00];
- v4l_dbg(1, debug, client, "changed input or channel\n");
- upd64031a_write(client, R00, reg | 0x10);
- upd64031a_write(client, R00, reg & ~0x10);
+ v4l2_dbg(1, debug, sd, "changed input or channel\n");
+ upd64031a_write(sd, R00, reg | 0x10);
+ upd64031a_write(sd, R00, reg & ~0x10);
+ return 0;
}
/* ------------------------------------------------------------------------ */
+static int upd64031a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct upd64031a_state *state = to_state(sd);
+ u8 r00, r05, r08;
+
+ state->gr_mode = (route->input & 3) << 6;
+ state->direct_3dycs_connect = (route->input & 0xc) << 4;
+ state->ext_comp_sync =
+ (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1;
+ state->ext_vert_sync =
+ (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2;
+ r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode;
+ r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) |
+ state->ext_comp_sync | state->ext_vert_sync;
+ r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) |
+ state->direct_3dycs_connect;
+ upd64031a_write(sd, R00, r00);
+ upd64031a_write(sd, R05, r05);
+ upd64031a_write(sd, R08, r08);
+ return upd64031a_s_frequency(sd, NULL);
+}
+
+static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0);
+}
+
+static int upd64031a_log_status(struct v4l2_subdev *sd)
+{
+ v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n",
+ upd64031a_read(sd, 0), upd64031a_read(sd, 1));
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = upd64031a_read(sd, reg->reg & 0xff);
+ return 0;
+}
+
+static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
static int upd64031a_command(struct i2c_client *client, unsigned cmd, void *arg)
{
- struct upd64031a_state *state = i2c_get_clientdata(client);
- struct v4l2_routing *route = arg;
-
- switch (cmd) {
- case VIDIOC_S_FREQUENCY:
- upd64031a_change(client);
- break;
-
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = (state->gr_mode >> 6) |
- (state->direct_3dycs_connect >> 4) |
- (state->ext_comp_sync >> 1) |
- (state->ext_vert_sync >> 2);
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- u8 r00, r05, r08;
-
- state->gr_mode = (route->input & 3) << 6;
- state->direct_3dycs_connect = (route->input & 0xc) << 4;
- state->ext_comp_sync =
- (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1;
- state->ext_vert_sync =
- (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2;
- r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode;
- r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) |
- state->ext_comp_sync | state->ext_vert_sync;
- r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) |
- state->direct_3dycs_connect;
- upd64031a_write(client, R00, r00);
- upd64031a_write(client, R05, r05);
- upd64031a_write(client, R08, r08);
- upd64031a_change(client);
- break;
- }
-
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Status: SA00=0x%02x SA01=0x%02x\n",
- upd64031a_read(client, 0), upd64031a_read(client, 1));
- break;
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+static const struct v4l2_subdev_core_ops upd64031a_core_ops = {
+ .log_status = upd64031a_log_status,
+ .g_chip_ident = upd64031a_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client,
- reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER) {
- reg->val = upd64031a_read(client, reg->reg & 0xff);
- break;
- }
- upd64031a_write(client, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
+ .g_register = upd64031a_g_register,
+ .s_register = upd64031a_s_register,
#endif
+};
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg,
- V4L2_IDENT_UPD64031A, 0);
+static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = {
+ .s_frequency = upd64031a_s_frequency,
+};
- default:
- break;
- }
- return 0;
-}
+static const struct v4l2_subdev_video_ops upd64031a_video_ops = {
+ .s_routing = upd64031a_s_routing,
+};
+
+static const struct v4l2_subdev_ops upd64031a_ops = {
+ .core = &upd64031a_core_ops,
+ .tuner = &upd64031a_tuner_ops,
+ .video = &upd64031a_video_ops,
+};
/* ------------------------------------------------------------------------ */
@@ -199,6 +227,7 @@ static int upd64031a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct upd64031a_state *state;
+ struct v4l2_subdev *sd;
int i;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -210,19 +239,23 @@ static int upd64031a_probe(struct i2c_client *client,
state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
- i2c_set_clientdata(client, state);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &upd64031a_ops);
memcpy(state->regs, upd64031a_init, sizeof(state->regs));
state->gr_mode = UPD64031A_GR_ON << 6;
state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4;
state->ext_comp_sync = state->ext_vert_sync = 0;
for (i = 0; i < TOT_REGS; i++)
- upd64031a_write(client, i, state->regs[i]);
+ upd64031a_write(sd, i, state->regs[i]);
return 0;
}
static int upd64031a_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c
index 9521ce004dcc..58412cb9c01a 100644
--- a/drivers/media/video/upd64083.c
+++ b/drivers/media/video/upd64083.c
@@ -26,7 +26,7 @@
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/upd64083.h>
@@ -51,11 +51,17 @@ enum {
};
struct upd64083_state {
+ struct v4l2_subdev sd;
u8 mode;
u8 ext_y_adc;
u8 regs[TOT_REGS];
};
+static inline struct upd64083_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct upd64083_state, sd);
+}
+
/* Initial values when used in combination with the
NEC upd64031a ghost reduction chip. */
static u8 upd64083_init[] = {
@@ -68,34 +74,24 @@ static u8 upd64083_init[] = {
/* ------------------------------------------------------------------------ */
-static void upd64083_log_status(struct i2c_client *client)
-{
- u8 buf[7];
-
- i2c_master_recv(client, buf, 7);
- v4l_info(client, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x "
- "SA04=%02x SA05=%02x SA06=%02x\n",
- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
-}
-
-/* ------------------------------------------------------------------------ */
-
-static void upd64083_write(struct i2c_client *client, u8 reg, u8 val)
+static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 buf[2];
buf[0] = reg;
buf[1] = val;
- v4l_dbg(1, debug, client, "write reg: %02x val: %02x\n", reg, val);
+ v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val);
if (i2c_master_send(client, buf, 2) != 2)
- v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val);
+ v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val);
}
/* ------------------------------------------------------------------------ */
#ifdef CONFIG_VIDEO_ADV_DEBUG
-static u8 upd64083_read(struct i2c_client *client, u8 reg)
+static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 buf[7];
if (reg >= sizeof(buf))
@@ -107,67 +103,94 @@ static u8 upd64083_read(struct i2c_client *client, u8 reg)
/* ------------------------------------------------------------------------ */
-static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int upd64083_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct upd64083_state *state = i2c_get_clientdata(client);
- struct v4l2_routing *route = arg;
-
- switch (cmd) {
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = (state->mode >> 6) | (state->ext_y_adc >> 3);
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- u8 r00, r02;
-
- if (route->input > 7 || (route->input & 6) == 6)
- return -EINVAL;
- state->mode = (route->input & 3) << 6;
- state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3;
- r00 = (state->regs[R00] & ~(3 << 6)) | state->mode;
- r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc;
- upd64083_write(client, R00, r00);
- upd64083_write(client, R02, r02);
- break;
- }
-
- case VIDIOC_LOG_STATUS:
- upd64083_log_status(client);
- break;
+ struct upd64083_state *state = to_state(sd);
+ u8 r00, r02;
+
+ if (route->input > 7 || (route->input & 6) == 6)
+ return -EINVAL;
+ state->mode = (route->input & 3) << 6;
+ state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3;
+ r00 = (state->regs[R00] & ~(3 << 6)) | state->mode;
+ r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc;
+ upd64083_write(sd, R00, r00);
+ upd64083_write(sd, R02, r02);
+ return 0;
+}
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
+static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = upd64083_read(sd, reg->reg & 0xff);
+ return 0;
+}
- if (!v4l2_chip_match_i2c_client(client,
+static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER) {
- reg->val = upd64083_read(client, reg->reg & 0xff);
- break;
- }
- upd64083_write(client, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
#endif
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg,
- V4L2_IDENT_UPD64083, 0);
+static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0);
+}
- default:
- break;
- }
+static int upd64083_log_status(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[7];
+ i2c_master_recv(client, buf, 7);
+ v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x "
+ "SA04=%02x SA05=%02x SA06=%02x\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
return 0;
}
+static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops upd64083_core_ops = {
+ .log_status = upd64083_log_status,
+ .g_chip_ident = upd64083_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = upd64083_g_register,
+ .s_register = upd64083_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops upd64083_video_ops = {
+ .s_routing = upd64083_s_routing,
+};
+
+static const struct v4l2_subdev_ops upd64083_ops = {
+ .core = &upd64083_core_ops,
+ .video = &upd64083_video_ops,
+};
+
/* ------------------------------------------------------------------------ */
/* i2c implementation */
@@ -176,6 +199,7 @@ static int upd64083_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct upd64083_state *state;
+ struct v4l2_subdev *sd;
int i;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -187,19 +211,23 @@ static int upd64083_probe(struct i2c_client *client,
state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
- i2c_set_clientdata(client, state);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &upd64083_ops);
/* Initially assume that a ghost reduction chip is present */
state->mode = 0; /* YCS mode */
state->ext_y_adc = (1 << 5);
memcpy(state->regs, upd64083_init, TOT_REGS);
for (i = 0; i < TOT_REGS; i++)
- upd64083_write(client, i, state->regs[i]);
+ upd64083_write(sd, i, state->regs[i]);
return 0;
}
static int upd64083_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/usbvideo/ibmcam.c b/drivers/media/video/usbvideo/ibmcam.c
index c710bcd1df48..f8d85ddb4804 100644
--- a/drivers/media/video/usbvideo/ibmcam.c
+++ b/drivers/media/video/usbvideo/ibmcam.c
@@ -3779,11 +3779,11 @@ static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id *
err("Alternate settings have different endpoint addresses!");
return -ENODEV;
}
- if ((endpoint->bmAttributes & 0x03) != 0x01) {
+ if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) {
err("Interface %d. has non-ISO endpoint!", ifnum);
return -ENODEV;
}
- if ((endpoint->bEndpointAddress & 0x80) == 0) {
+ if (usb_endpoint_dir_out(endpoint)) {
err("Interface %d. has ISO OUT endpoint!", ifnum);
return -ENODEV;
}
diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c
index da27a5287983..90f0ce6a26bc 100644
--- a/drivers/media/video/usbvideo/konicawc.c
+++ b/drivers/media/video/usbvideo/konicawc.c
@@ -823,12 +823,12 @@ static int konicawc_probe(struct usb_interface *intf, const struct usb_device_id
err("Alternate settings have different endpoint addresses!");
return -ENODEV;
}
- if ((endpoint->bmAttributes & 0x03) != 0x01) {
+ if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) {
err("Interface %d. has non-ISO endpoint!",
interface->desc.bInterfaceNumber);
return -ENODEV;
}
- if ((endpoint->bEndpointAddress & 0x80) == 0) {
+ if (usb_endpoint_dir_out(endpoint)) {
err("Interface %d. has ISO OUT endpoint!",
interface->desc.bInterfaceNumber);
return -ENODEV;
diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c
index 4459b8a7f818..fd112f0b9d35 100644
--- a/drivers/media/video/usbvideo/quickcam_messenger.c
+++ b/drivers/media/video/usbvideo/quickcam_messenger.c
@@ -447,7 +447,7 @@ static int qcm_sensor_init(struct uvd *uvd)
CHECK_RET(ret, qcm_stv_setw(uvd->dev, 0x15c1,
cpu_to_le16(ISOC_PACKET_SIZE)));
CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x15c3, 0x08));
- CHECK_RET(ret, ret = qcm_stv_setb(uvd->dev, 0x143f, 0x01));
+ CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143f, 0x01));
CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00));
@@ -955,8 +955,7 @@ static int qcm_probe(struct usb_interface *intf,
for (j=0; j < interface->desc.bNumEndpoints; j++) {
endpoint = &interface->endpoint[j].desc;
- if ((endpoint->bEndpointAddress &
- USB_ENDPOINT_DIR_MASK) != USB_DIR_IN)
+ if (usb_endpoint_dir_out(endpoint))
continue; /* not input then not good */
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
@@ -965,9 +964,7 @@ static int qcm_probe(struct usb_interface *intf,
continue; /* 0 pkt size is not what we want */
}
- if ((endpoint->bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) ==
- USB_ENDPOINT_XFER_ISOC) {
+ if (usb_endpoint_xfer_isoc(endpoint)) {
video_ep = endpoint->bEndpointAddress;
/* break out of the search */
goto good_videoep;
diff --git a/drivers/media/video/usbvideo/ultracam.c b/drivers/media/video/usbvideo/ultracam.c
index 9714baab7833..839a08240c25 100644
--- a/drivers/media/video/usbvideo/ultracam.c
+++ b/drivers/media/video/usbvideo/ultracam.c
@@ -556,12 +556,12 @@ static int ultracam_probe(struct usb_interface *intf, const struct usb_device_id
err("Alternate settings have different endpoint addresses!");
return -ENODEV;
}
- if ((endpoint->bmAttributes & 0x03) != 0x01) {
+ if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) {
err("Interface %d. has non-ISO endpoint!",
interface->desc.bInterfaceNumber);
return -ENODEV;
}
- if ((endpoint->bEndpointAddress & 0x80) == 0) {
+ if (usb_endpoint_dir_out(endpoint)) {
err("Interface %d. has ISO OUT endpoint!",
interface->desc.bInterfaceNumber);
return -ENODEV;
diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c
index 7c575bb8184f..148a1f98c70f 100644
--- a/drivers/media/video/usbvideo/usbvideo.c
+++ b/drivers/media/video/usbvideo/usbvideo.c
@@ -1123,7 +1123,7 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file)
if (uvd->debug > 1)
dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev);
- if (0 < usbvideo_ClientIncModCount(uvd))
+ if (usbvideo_ClientIncModCount(uvd) < 0)
return -ENODEV;
mutex_lock(&uvd->lock);
@@ -1281,8 +1281,7 @@ static int usbvideo_v4l_close(struct inode *inode, struct file *file)
* History:
* 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings.
*/
-static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int usbvideo_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct uvd *uvd = file->private_data;
@@ -1505,7 +1504,7 @@ static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file,
static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, usbvideo_v4l_do_ioctl);
+ return video_usercopy(file, cmd, arg, usbvideo_v4l_do_ioctl);
}
/*
diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c
index 8e2d58bec481..4602597ed8d1 100644
--- a/drivers/media/video/usbvideo/vicam.c
+++ b/drivers/media/video/usbvideo/vicam.c
@@ -844,8 +844,7 @@ vicam_probe( struct usb_interface *intf, const struct usb_device_id *id)
interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints));
endpoint = &interface->endpoint[0].desc;
- if ((endpoint->bEndpointAddress & 0x80) &&
- ((endpoint->bmAttributes & 3) == 0x02)) {
+ if (usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
bulkEndpoint = endpoint->bEndpointAddress;
} else {
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index d185b57fdcd0..85661b1848fe 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -523,7 +523,7 @@ static int vidioc_querycap (struct file *file, void *priv,
strlcpy(vc->card,
usbvision_device_data[usbvision->DevModel].ModelString,
sizeof(vc->card));
- strlcpy(vc->bus_info, usbvision->dev->dev.bus_id,
+ strlcpy(vc->bus_info, dev_name(&usbvision->dev->dev),
sizeof(vc->bus_info));
vc->version = USBVISION_DRIVER_VERSION;
vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
@@ -1278,7 +1278,7 @@ static int usbvision_vbi_close(struct inode *inode, struct file *file)
return -ENODEV;
}
-static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file,
+static int usbvision_do_vbi_ioctl(struct file *file,
unsigned int cmd, void *arg)
{
/* TODO */
@@ -1288,7 +1288,7 @@ static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file,
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);
+ return video_usercopy(file, cmd, arg, usbvision_do_vbi_ioctl);
}
@@ -1679,7 +1679,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf,
interface = &dev->actconfig->interface[ifnum]->altsetting[0];
}
endpoint = &interface->endpoint[1].desc;
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
+ if (usb_endpoint_type(endpoint) !=
USB_ENDPOINT_XFER_ISOC) {
err("%s: interface %d. has non-ISO endpoint!",
__func__, ifnum);
@@ -1687,8 +1687,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf,
__func__, endpoint->bmAttributes);
return -ENODEV;
}
- if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
- USB_DIR_OUT) {
+ if (usb_endpoint_dir_out(endpoint)) {
err("%s: interface %d. has ISO OUT endpoint!",
__func__, ifnum);
return -ENODEV;
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index f16aafe9cf14..2208165aa6f0 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -327,6 +327,31 @@ static struct uvc_menu_info exposure_auto_controls[] = {
{ 8, "Aperture Priority Mode" },
};
+static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
+ __u8 query, const __u8 *data)
+{
+ __s8 zoom = (__s8)data[0];
+
+ switch (query) {
+ case GET_CUR:
+ return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]);
+
+ case GET_MIN:
+ case GET_MAX:
+ case GET_RES:
+ case GET_DEF:
+ default:
+ return data[2];
+ }
+}
+
+static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping,
+ __s32 value, __u8 *data)
+{
+ data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff;
+ data[2] = min(abs(value), 0xff);
+}
+
static struct uvc_control_mapping uvc_ctrl_mappings[] = {
{
.id = V4L2_CID_BRIGHTNESS,
@@ -532,6 +557,38 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
},
+ {
+ .id = V4L2_CID_ZOOM_ABSOLUTE,
+ .name = "Zoom, Absolute",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ZOOM_ABSOLUTE_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_ZOOM_CONTINUOUS,
+ .name = "Zoom, Continuous",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ZOOM_RELATIVE_CONTROL,
+ .size = 0,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ .get = uvc_ctrl_get_zoom,
+ .set = uvc_ctrl_set_zoom,
+ },
+ {
+ .id = V4L2_CID_PRIVACY,
+ .name = "Privacy",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_PRIVACY_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
};
/* ------------------------------------------------------------------------
@@ -543,18 +600,23 @@ static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
return ctrl->data + id * ctrl->info->size;
}
-static inline int uvc_get_bit(const __u8 *data, int bit)
+static inline int uvc_test_bit(const __u8 *data, int bit)
{
return (data[bit >> 3] >> (bit & 7)) & 1;
}
+static inline void uvc_clear_bit(__u8 *data, int bit)
+{
+ data[bit >> 3] &= ~(1 << (bit & 7));
+}
+
/* Extract the bit string specified by mapping->offset and mapping->size
* from the little-endian data stored at 'data' and return the result as
* a signed 32bit integer. Sign extension will be performed if the mapping
* references a signed data type.
*/
-static __s32 uvc_get_le_value(const __u8 *data,
- struct uvc_control_mapping *mapping)
+static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
+ __u8 query, const __u8 *data)
{
int bits = mapping->size;
int offset = mapping->offset;
@@ -583,8 +645,8 @@ static __s32 uvc_get_le_value(const __u8 *data,
/* Set the bit string specified by mapping->offset and mapping->size
* in the little-endian data stored at 'data' to the value 'value'.
*/
-static void uvc_set_le_value(__s32 value, __u8 *data,
- struct uvc_control_mapping *mapping)
+static void uvc_set_le_value(struct uvc_control_mapping *mapping,
+ __s32 value, __u8 *data)
{
int bits = mapping->size;
int offset = mapping->offset;
@@ -736,7 +798,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
video->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size)) < 0)
goto out;
- v4l2_ctrl->default_value = uvc_get_le_value(data, mapping);
+ v4l2_ctrl->default_value = mapping->get(mapping, GET_DEF, data);
}
switch (mapping->v4l2_type) {
@@ -772,21 +834,21 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
video->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size)) < 0)
goto out;
- v4l2_ctrl->minimum = uvc_get_le_value(data, mapping);
+ v4l2_ctrl->minimum = mapping->get(mapping, GET_MIN, data);
}
if (ctrl->info->flags & UVC_CONTROL_GET_MAX) {
if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size)) < 0)
goto out;
- v4l2_ctrl->maximum = uvc_get_le_value(data, mapping);
+ v4l2_ctrl->maximum = mapping->get(mapping, GET_MAX, data);
}
if (ctrl->info->flags & UVC_CONTROL_GET_RES) {
if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size)) < 0)
goto out;
- v4l2_ctrl->step = uvc_get_le_value(data, mapping);
+ v4l2_ctrl->step = mapping->get(mapping, GET_RES, data);
}
ret = 0;
@@ -923,8 +985,8 @@ int uvc_ctrl_get(struct uvc_video_device *video,
ctrl->loaded = 1;
}
- xctrl->value = uvc_get_le_value(
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping);
+ xctrl->value = mapping->get(mapping, GET_CUR,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
menu = mapping->menu_info;
@@ -980,8 +1042,8 @@ int uvc_ctrl_set(struct uvc_video_device *video,
ctrl->info->size);
}
- uvc_set_le_value(value,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping);
+ mapping->set(mapping, value,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
ctrl->dirty = 1;
ctrl->modified = 1;
@@ -1257,6 +1319,11 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping)
struct uvc_control_mapping *map;
int ret = -EINVAL;
+ if (mapping->get == NULL)
+ mapping->get = uvc_get_le_value;
+ if (mapping->set == NULL)
+ mapping->set = uvc_set_le_value;
+
if (mapping->id & ~V4L2_CTRL_ID_MASK) {
uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with "
"invalid control id 0x%08x\n", mapping->name,
@@ -1306,6 +1373,51 @@ end:
}
/*
+ * Prune an entity of its bogus controls. This currently includes processing
+ * unit auto controls for which no corresponding manual control is available.
+ * Such auto controls make little sense if any, and are known to crash at
+ * least the SiGma Micro webcam.
+ */
+static void
+uvc_ctrl_prune_entity(struct uvc_entity *entity)
+{
+ static const struct {
+ u8 idx_manual;
+ u8 idx_auto;
+ } blacklist[] = {
+ { 2, 11 }, /* Hue */
+ { 6, 12 }, /* White Balance Temperature */
+ { 7, 13 }, /* White Balance Component */
+ };
+
+ u8 *controls;
+ unsigned int size;
+ unsigned int i;
+
+ if (UVC_ENTITY_TYPE(entity) != VC_PROCESSING_UNIT)
+ return;
+
+ controls = entity->processing.bmControls;
+ size = entity->processing.bControlSize;
+
+ for (i = 0; i < ARRAY_SIZE(blacklist); ++i) {
+ if (blacklist[i].idx_auto >= 8 * size ||
+ blacklist[i].idx_manual >= 8 * size)
+ continue;
+
+ if (!uvc_test_bit(controls, blacklist[i].idx_auto) ||
+ uvc_test_bit(controls, blacklist[i].idx_manual))
+ continue;
+
+ uvc_trace(UVC_TRACE_CONTROL, "Auto control %u/%u has no "
+ "matching manual control, removing it.\n", entity->id,
+ blacklist[i].idx_auto);
+
+ uvc_clear_bit(controls, blacklist[i].idx_auto);
+ }
+}
+
+/*
* Initialize device controls.
*/
int uvc_ctrl_init_device(struct uvc_device *dev)
@@ -1331,6 +1443,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
bControlSize = entity->camera.bControlSize;
}
+ if (dev->quirks & UVC_QUIRK_PRUNE_CONTROLS)
+ uvc_ctrl_prune_entity(entity);
+
for (i = 0; i < bControlSize; ++i)
ncontrols += hweight8(bmControls[i]);
@@ -1345,7 +1460,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
ctrl = entity->controls;
for (i = 0; i < bControlSize * 8; ++i) {
- if (uvc_get_bit(bmControls, i) == 0)
+ if (uvc_test_bit(bmControls, i) == 0)
continue;
ctrl->entity = entity;
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index d7ad060640bc..89d8bd10a852 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -12,8 +12,8 @@
*/
/*
- * This driver aims to support video input devices compliant with the 'USB
- * Video Class' specification.
+ * This driver aims to support video input and ouput devices compliant with the
+ * 'USB Video Class' specification.
*
* The driver doesn't support the deprecated v4l1 interface. It implements the
* mmap capture method only, and doesn't do any image format conversion in
@@ -32,6 +32,7 @@
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <asm/atomic.h>
+#include <asm/unaligned.h>
#include <media/v4l2-common.h>
@@ -43,6 +44,7 @@
#define DRIVER_VERSION "v0.1.0"
#endif
+unsigned int uvc_no_drop_param;
static unsigned int uvc_quirks_param;
unsigned int uvc_trace_param;
@@ -288,8 +290,10 @@ static int uvc_parse_format(struct uvc_device *dev,
struct uvc_format_desc *fmtdesc;
struct uvc_frame *frame;
const unsigned char *start = buffer;
+ unsigned char *_buffer;
unsigned int interval;
unsigned int i, n;
+ int _buflen;
__u8 ftype;
format->type = buffer[2];
@@ -410,12 +414,20 @@ static int uvc_parse_format(struct uvc_device *dev,
buflen -= buffer[0];
buffer += buffer[0];
+ /* Count the number of frame descriptors to test the bFrameIndex
+ * field when parsing the descriptors. We can't rely on the
+ * bNumFrameDescriptors field as some cameras don't initialize it
+ * properly.
+ */
+ for (_buflen = buflen, _buffer = buffer;
+ _buflen > 2 && _buffer[2] == ftype;
+ _buflen -= _buffer[0], _buffer += _buffer[0])
+ format->nframes++;
+
/* Parse the frame descriptors. Only uncompressed, MJPEG and frame
* based formats have frame descriptors.
*/
while (buflen > 2 && buffer[2] == ftype) {
- frame = &format->frame[format->nframes];
-
if (ftype != VS_FRAME_FRAME_BASED)
n = buflen > 25 ? buffer[25] : 0;
else
@@ -430,22 +442,32 @@ static int uvc_parse_format(struct uvc_device *dev,
return -EINVAL;
}
+ if (buffer[3] - 1 >= format->nframes) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+ "interface %d frame index %u out of range\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber,
+ buffer[3]);
+ return -EINVAL;
+ }
+
+ frame = &format->frame[buffer[3] - 1];
+
frame->bFrameIndex = buffer[3];
frame->bmCapabilities = buffer[4];
- frame->wWidth = le16_to_cpup((__le16 *)&buffer[5]);
- frame->wHeight = le16_to_cpup((__le16 *)&buffer[7]);
- frame->dwMinBitRate = le32_to_cpup((__le32 *)&buffer[9]);
- frame->dwMaxBitRate = le32_to_cpup((__le32 *)&buffer[13]);
+ frame->wWidth = get_unaligned_le16(&buffer[5]);
+ frame->wHeight = get_unaligned_le16(&buffer[7]);
+ frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
+ frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
if (ftype != VS_FRAME_FRAME_BASED) {
frame->dwMaxVideoFrameBufferSize =
- le32_to_cpup((__le32 *)&buffer[17]);
+ get_unaligned_le32(&buffer[17]);
frame->dwDefaultFrameInterval =
- le32_to_cpup((__le32 *)&buffer[21]);
+ get_unaligned_le32(&buffer[21]);
frame->bFrameIntervalType = buffer[25];
} else {
frame->dwMaxVideoFrameBufferSize = 0;
frame->dwDefaultFrameInterval =
- le32_to_cpup((__le32 *)&buffer[17]);
+ get_unaligned_le32(&buffer[17]);
frame->bFrameIntervalType = buffer[21];
}
frame->dwFrameInterval = *intervals;
@@ -468,7 +490,7 @@ static int uvc_parse_format(struct uvc_device *dev,
* some other divisions by zero which could happen.
*/
for (i = 0; i < n; ++i) {
- interval = le32_to_cpup((__le32 *)&buffer[26+4*i]);
+ interval = get_unaligned_le32(&buffer[26+4*i]);
*(*intervals)++ = interval ? interval : 1;
}
@@ -486,7 +508,6 @@ static int uvc_parse_format(struct uvc_device *dev,
10000000/frame->dwDefaultFrameInterval,
(100000000/frame->dwDefaultFrameInterval)%10);
- format->nframes++;
buflen -= buffer[0];
buffer += buffer[0];
}
@@ -588,46 +609,55 @@ static int uvc_parse_streaming(struct uvc_device *dev,
}
/* Parse the header descriptor. */
- if (buffer[2] == VS_OUTPUT_HEADER) {
+ switch (buffer[2]) {
+ case VS_OUTPUT_HEADER:
+ streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ size = 9;
+ break;
+
+ case VS_INPUT_HEADER:
+ streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ size = 13;
+ break;
+
+ default:
uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
- "%d OUTPUT HEADER descriptor is not supported.\n",
- dev->udev->devnum, alts->desc.bInterfaceNumber);
+ "%d HEADER descriptor not found.\n", dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
goto error;
- } else if (buffer[2] == VS_INPUT_HEADER) {
- p = buflen >= 5 ? buffer[3] : 0;
- n = buflen >= 12 ? buffer[12] : 0;
+ }
- if (buflen < 13 + p*n || buffer[2] != VS_INPUT_HEADER) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d INPUT HEADER descriptor is "
- "invalid.\n", dev->udev->devnum,
- alts->desc.bInterfaceNumber);
- goto error;
- }
+ p = buflen >= 4 ? buffer[3] : 0;
+ n = buflen >= size ? buffer[size-1] : 0;
+
+ if (buflen < size + p*n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d HEADER descriptor is invalid.\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber);
+ goto error;
+ }
- streaming->header.bNumFormats = p;
- streaming->header.bEndpointAddress = buffer[6];
+ streaming->header.bNumFormats = p;
+ streaming->header.bEndpointAddress = buffer[6];
+ if (buffer[2] == VS_INPUT_HEADER) {
streaming->header.bmInfo = buffer[7];
streaming->header.bTerminalLink = buffer[8];
streaming->header.bStillCaptureMethod = buffer[9];
streaming->header.bTriggerSupport = buffer[10];
streaming->header.bTriggerUsage = buffer[11];
- streaming->header.bControlSize = n;
-
- streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL);
- if (streaming->header.bmaControls == NULL) {
- ret = -ENOMEM;
- goto error;
- }
-
- memcpy(streaming->header.bmaControls, &buffer[13], p*n);
} else {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
- "%d HEADER descriptor not found.\n", dev->udev->devnum,
- alts->desc.bInterfaceNumber);
+ streaming->header.bTerminalLink = buffer[7];
+ }
+ streaming->header.bControlSize = n;
+
+ streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL);
+ if (streaming->header.bmaControls == NULL) {
+ ret = -ENOMEM;
goto error;
}
+ memcpy(streaming->header.bmaControls, &buffer[size], p*n);
+
buflen -= buffer[0];
buffer += buffer[0];
@@ -813,8 +843,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev,
unit->type = VC_EXTENSION_UNIT;
memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
unit->extension.bNumControls = buffer[20];
- unit->extension.bNrInPins =
- le16_to_cpup((__le16 *)&buffer[21]);
+ unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]);
unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;
memcpy(unit->extension.baSourceID, &buffer[22], p);
unit->extension.bControlSize = buffer[22+p];
@@ -858,8 +887,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
return -EINVAL;
}
- dev->uvc_version = le16_to_cpup((__le16 *)&buffer[3]);
- dev->clock_frequency = le32_to_cpup((__le32 *)&buffer[7]);
+ dev->uvc_version = get_unaligned_le16(&buffer[3]);
+ dev->clock_frequency = get_unaligned_le32(&buffer[7]);
/* Parse all USB Video Streaming interfaces. */
for (i = 0; i < n; ++i) {
@@ -886,7 +915,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
/* Make sure the terminal type MSB is not null, otherwise it
* could be confused with a unit.
*/
- type = le16_to_cpup((__le16 *)&buffer[4]);
+ type = get_unaligned_le16(&buffer[4]);
if ((type & 0xff00) == 0) {
uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
"interface %d INPUT_TERMINAL %d has invalid "
@@ -928,11 +957,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
term->camera.bControlSize = n;
term->camera.bmControls = (__u8 *)term + sizeof *term;
term->camera.wObjectiveFocalLengthMin =
- le16_to_cpup((__le16 *)&buffer[8]);
+ get_unaligned_le16(&buffer[8]);
term->camera.wObjectiveFocalLengthMax =
- le16_to_cpup((__le16 *)&buffer[10]);
+ get_unaligned_le16(&buffer[10]);
term->camera.wOcularFocalLength =
- le16_to_cpup((__le16 *)&buffer[12]);
+ get_unaligned_le16(&buffer[12]);
memcpy(term->camera.bmControls, &buffer[15], n);
} else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) {
term->media.bControlSize = n;
@@ -968,7 +997,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
/* Make sure the terminal type MSB is not null, otherwise it
* could be confused with a unit.
*/
- type = le16_to_cpup((__le16 *)&buffer[4]);
+ type = get_unaligned_le16(&buffer[4]);
if ((type & 0xff00) == 0) {
uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
"interface %d OUTPUT_TERMINAL %d has invalid "
@@ -1042,7 +1071,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
unit->type = buffer[2];
unit->processing.bSourceID = buffer[4];
unit->processing.wMaxMultiplier =
- le16_to_cpup((__le16 *)&buffer[5]);
+ get_unaligned_le16(&buffer[5]);
unit->processing.bControlSize = buffer[7];
unit->processing.bmControls = (__u8 *)unit + sizeof *unit;
memcpy(unit->processing.bmControls, &buffer[8], n);
@@ -1077,8 +1106,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
unit->type = buffer[2];
memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
unit->extension.bNumControls = buffer[20];
- unit->extension.bNrInPins =
- le16_to_cpup((__le16 *)&buffer[21]);
+ unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]);
unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;
memcpy(unit->extension.baSourceID, &buffer[22], p);
unit->extension.bControlSize = buffer[22+p];
@@ -1128,8 +1156,13 @@ next_descriptor:
buffer += buffer[0];
}
- /* Check if the optional status endpoint is present. */
- if (alts->desc.bNumEndpoints == 1) {
+ /* Check if the optional status endpoint is present. Built-in iSight
+ * webcams have an interrupt endpoint but spit proprietary data that
+ * don't conform to the UVC status endpoint messages. Don't try to
+ * handle the interrupt endpoint for those cameras.
+ */
+ if (alts->desc.bNumEndpoints == 1 &&
+ !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) {
struct usb_host_endpoint *ep = &alts->endpoint[0];
struct usb_endpoint_descriptor *desc = &ep->desc;
@@ -1234,6 +1267,26 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
list_add_tail(&entity->chain, &video->iterms);
break;
+ case TT_STREAMING:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- IT %d\n", entity->id);
+
+ if (!UVC_ENTITY_IS_ITERM(entity)) {
+ uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
+ "terminal %u.\n", entity->id);
+ return -1;
+ }
+
+ if (video->sterm != NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found multiple streaming "
+ "entities in chain.\n");
+ return -1;
+ }
+
+ list_add_tail(&entity->chain, &video->iterms);
+ video->sterm = entity;
+ break;
+
default:
uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
"0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
@@ -1344,6 +1397,10 @@ static int uvc_scan_chain(struct uvc_video_device *video)
entity = video->oterm;
uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
+
+ if (UVC_ENTITY_TYPE(entity) == TT_STREAMING)
+ video->sterm = entity;
+
id = entity->output.bSourceID;
while (id != 0) {
prev = entity;
@@ -1372,8 +1429,11 @@ static int uvc_scan_chain(struct uvc_video_device *video)
return id;
}
- /* Initialize the video buffers queue. */
- uvc_queue_init(&video->queue);
+ if (video->sterm == NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in "
+ "chain.\n");
+ return -1;
+ }
return 0;
}
@@ -1384,7 +1444,8 @@ static int uvc_scan_chain(struct uvc_video_device *video)
* The driver currently supports a single video device per control interface
* only. The terminal and units must match the following structure:
*
- * ITT_CAMERA -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
+ * ITT_* -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
+ * TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_*
*
* The Extension Units, if present, must have a single input pin. The
* Processing Unit and Extension Units can be in any order. Additional
@@ -1401,7 +1462,7 @@ static int uvc_register_video(struct uvc_device *dev)
list_for_each_entry(term, &dev->entities, list) {
struct uvc_streaming *streaming;
- if (UVC_ENTITY_TYPE(term) != TT_STREAMING)
+ if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term))
continue;
memset(&dev->video, 0, sizeof dev->video);
@@ -1414,7 +1475,8 @@ static int uvc_register_video(struct uvc_device *dev)
continue;
list_for_each_entry(streaming, &dev->streaming, list) {
- if (streaming->header.bTerminalLink == term->id) {
+ if (streaming->header.bTerminalLink ==
+ dev->video.sterm->id) {
dev->video.streaming = streaming;
found = 1;
break;
@@ -1440,6 +1502,9 @@ static int uvc_register_video(struct uvc_device *dev)
printk(" -> %d).\n", dev->video.oterm->id);
}
+ /* Initialize the video buffers queue. */
+ uvc_queue_init(&dev->video.queue, dev->video.streaming->type);
+
/* Initialize the streaming interface with default streaming
* parameters.
*/
@@ -1707,24 +1772,6 @@ static int uvc_reset_resume(struct usb_interface *intf)
* though they are compliant.
*/
static struct usb_device_id uvc_ids[] = {
- /* ALi M5606 (Clevo M540SR) */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x0402,
- .idProduct = 0x5606,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Creative Live! Optia */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x041e,
- .idProduct = 0x4057,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Microsoft Lifecam NX-6000 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1810,15 +1857,6 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* Silicon Motion SM371 */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x090c,
- .idProduct = 0xb371,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
/* MT6227 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1837,6 +1875,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (Samsung Q310) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x5931,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Asus F9SG */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1855,6 +1902,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Lenovo Thinkpad SL500 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x17ef,
+ .idProduct = 0x480b,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Ecamm Pico iMage */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1884,106 +1940,8 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX
- | UVC_QUIRK_IGNORE_SELECTOR_UNIT},
- /* Acer OEM Webcam - Unknown vendor */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0100,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Packard Bell OEM Webcam - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0101,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Acer Crystal Eye webcam - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0102,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Compaq Presario B1200 - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0104,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Acer Travelmate 7720 - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0105,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Medion Akoya Mini E1210 - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0141,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Acer OrbiCam - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0200,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Fujitsu Amilo SI2636 - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0202,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Advent 4211 - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0203,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0300,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Clevo M570TU - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0303,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ | UVC_QUIRK_IGNORE_SELECTOR_UNIT
+ | UVC_QUIRK_PRUNE_CONTROLS },
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
{}
@@ -2029,6 +1987,8 @@ static void __exit uvc_cleanup(void)
module_init(uvc_init);
module_exit(uvc_cleanup);
+module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(quirks, "Forced device quirks");
module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index 5646a6a32939..42546342e97d 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -79,12 +79,13 @@
*
*/
-void uvc_queue_init(struct uvc_video_queue *queue)
+void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
{
mutex_init(&queue->mutex);
spin_lock_init(&queue->irqlock);
INIT_LIST_HEAD(&queue->mainqueue);
INIT_LIST_HEAD(&queue->irqqueue);
+ queue->type = type;
}
/*
@@ -132,7 +133,7 @@ int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers,
queue->buffer[i].buf.index = i;
queue->buffer[i].buf.m.offset = i * bufsize;
queue->buffer[i].buf.length = buflength;
- queue->buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ queue->buffer[i].buf.type = queue->type;
queue->buffer[i].buf.sequence = 0;
queue->buffer[i].buf.field = V4L2_FIELD_NONE;
queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
@@ -226,7 +227,7 @@ int uvc_queue_buffer(struct uvc_video_queue *queue,
uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index);
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ if (v4l2_buf->type != queue->type ||
v4l2_buf->memory != V4L2_MEMORY_MMAP) {
uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
"and/or memory (%u).\n", v4l2_buf->type,
@@ -249,6 +250,13 @@ int uvc_queue_buffer(struct uvc_video_queue *queue,
goto done;
}
+ if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ v4l2_buf->bytesused > buf->buf.length) {
+ uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
spin_lock_irqsave(&queue->irqlock, flags);
if (queue->flags & UVC_QUEUE_DISCONNECTED) {
spin_unlock_irqrestore(&queue->irqlock, flags);
@@ -256,7 +264,11 @@ int uvc_queue_buffer(struct uvc_video_queue *queue,
goto done;
}
buf->state = UVC_BUF_STATE_QUEUED;
- buf->buf.bytesused = 0;
+ if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ buf->buf.bytesused = 0;
+ else
+ buf->buf.bytesused = v4l2_buf->bytesused;
+
list_add_tail(&buf->stream, &queue->mainqueue);
list_add_tail(&buf->queue, &queue->irqqueue);
spin_unlock_irqrestore(&queue->irqlock, flags);
@@ -289,7 +301,7 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf;
int ret = 0;
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ if (v4l2_buf->type != queue->type ||
v4l2_buf->memory != V4L2_MEMORY_MMAP) {
uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
"and/or memory (%u).\n", v4l2_buf->type,
@@ -397,6 +409,7 @@ int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
}
queue->sequence = 0;
queue->flags |= UVC_QUEUE_STREAMING;
+ queue->buf_used = 0;
} else {
uvc_queue_cancel(queue, 0);
INIT_LIST_HEAD(&queue->mainqueue);
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index 758dfefaba8d..afcc6934559e 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -110,7 +110,7 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video,
int ret = 0;
__u8 *fcc;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (fmt->type != video->streaming->type)
return -EINVAL;
fcc = (__u8 *)&fmt->fmt.pix.pixelformat;
@@ -216,7 +216,7 @@ static int uvc_v4l2_get_format(struct uvc_video_device *video,
struct uvc_format *format = video->streaming->cur_format;
struct uvc_frame *frame = video->streaming->cur_frame;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (fmt->type != video->streaming->type)
return -EINVAL;
if (format == NULL || frame == NULL)
@@ -242,7 +242,7 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video,
struct uvc_frame *frame;
int ret;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (fmt->type != video->streaming->type)
return -EINVAL;
if (uvc_queue_streaming(&video->queue))
@@ -252,9 +252,6 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video,
if (ret < 0)
return ret;
- if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0)
- return ret;
-
memcpy(&video->streaming->ctrl, &probe, sizeof probe);
video->streaming->cur_format = format;
video->streaming->cur_frame = frame;
@@ -267,7 +264,7 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
{
uint32_t numerator, denominator;
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (parm->type != video->streaming->type)
return -EINVAL;
numerator = video->streaming->ctrl.dwFrameInterval;
@@ -275,13 +272,21 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
uvc_simplify_fraction(&numerator, &denominator, 8, 333);
memset(parm, 0, sizeof *parm);
- parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- parm->parm.capture.capturemode = 0;
- parm->parm.capture.timeperframe.numerator = numerator;
- parm->parm.capture.timeperframe.denominator = denominator;
- parm->parm.capture.extendedmode = 0;
- parm->parm.capture.readbuffers = 0;
+ parm->type = video->streaming->type;
+
+ if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ parm->parm.capture.capturemode = 0;
+ parm->parm.capture.timeperframe.numerator = numerator;
+ parm->parm.capture.timeperframe.denominator = denominator;
+ parm->parm.capture.extendedmode = 0;
+ parm->parm.capture.readbuffers = 0;
+ } else {
+ parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ parm->parm.output.outputmode = 0;
+ parm->parm.output.timeperframe.numerator = numerator;
+ parm->parm.output.timeperframe.denominator = denominator;
+ }
return 0;
}
@@ -291,42 +296,45 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
{
struct uvc_frame *frame = video->streaming->cur_frame;
struct uvc_streaming_control probe;
+ struct v4l2_fract timeperframe;
uint32_t interval;
int ret;
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (parm->type != video->streaming->type)
return -EINVAL;
if (uvc_queue_streaming(&video->queue))
return -EBUSY;
+ if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ timeperframe = parm->parm.capture.timeperframe;
+ else
+ timeperframe = parm->parm.output.timeperframe;
+
memcpy(&probe, &video->streaming->ctrl, sizeof probe);
- interval = uvc_fraction_to_interval(
- parm->parm.capture.timeperframe.numerator,
- parm->parm.capture.timeperframe.denominator);
+ interval = uvc_fraction_to_interval(timeperframe.numerator,
+ timeperframe.denominator);
uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n",
- parm->parm.capture.timeperframe.numerator,
- parm->parm.capture.timeperframe.denominator,
- interval);
+ timeperframe.numerator, timeperframe.denominator, interval);
probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);
/* Probe the device with the new settings. */
if ((ret = uvc_probe_video(video, &probe)) < 0)
return ret;
- /* Commit the new settings. */
- if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0)
- return ret;
-
memcpy(&video->streaming->ctrl, &probe, sizeof probe);
/* Return the actual frame period. */
- parm->parm.capture.timeperframe.numerator = probe.dwFrameInterval;
- parm->parm.capture.timeperframe.denominator = 10000000;
- uvc_simplify_fraction(&parm->parm.capture.timeperframe.numerator,
- &parm->parm.capture.timeperframe.denominator,
- 8, 333);
+ timeperframe.numerator = probe.dwFrameInterval;
+ timeperframe.denominator = 10000000;
+ uvc_simplify_fraction(&timeperframe.numerator,
+ &timeperframe.denominator, 8, 333);
+
+ if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ parm->parm.capture.timeperframe = timeperframe;
+ else
+ parm->parm.output.timeperframe = timeperframe;
return 0;
}
@@ -464,17 +472,13 @@ static int uvc_v4l2_release(struct inode *inode, struct file *file)
return 0;
}
-static int __uvc_v4l2_do_ioctl(struct file *file,
- unsigned int cmd, void *arg)
+static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = video_devdata(file);
struct uvc_video_device *video = video_get_drvdata(vdev);
struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
int ret = 0;
- if (uvc_trace_param & UVC_TRACE_IOCTL)
- v4l_printk_ioctl(cmd);
-
switch (cmd) {
/* Query capabilities */
case VIDIOC_QUERYCAP:
@@ -487,8 +491,12 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
strncpy(cap->bus_info, video->dev->udev->bus->bus_name,
sizeof cap->bus_info);
cap->version = DRIVER_VERSION_NUMBER;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
- | V4L2_CAP_STREAMING;
+ if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+ | V4L2_CAP_STREAMING;
+ else
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT
+ | V4L2_CAP_STREAMING;
break;
}
@@ -666,7 +674,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
struct v4l2_fmtdesc *fmt = arg;
struct uvc_format *format;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ if (fmt->type != video->streaming->type ||
fmt->index >= video->streaming->nformats)
return -EINVAL;
@@ -805,7 +813,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
struct v4l2_cropcap *ccap = arg;
struct uvc_frame *frame = video->streaming->cur_frame;
- if (ccap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (ccap->type != video->streaming->type)
return -EINVAL;
ccap->bounds.left = 0;
@@ -831,7 +839,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
unsigned int bufsize =
video->streaming->ctrl.dwMaxVideoFrameSize;
- if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ if (rb->type != video->streaming->type ||
rb->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
@@ -851,7 +859,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
{
struct v4l2_buffer *buf = arg;
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (buf->type != video->streaming->type)
return -EINVAL;
if (!uvc_has_privileges(handle))
@@ -877,7 +885,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
{
int *type = arg;
- if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (*type != video->streaming->type)
return -EINVAL;
if (!uvc_has_privileges(handle))
@@ -892,7 +900,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
{
int *type = arg;
- if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (*type != video->streaming->type)
return -EINVAL;
if (!uvc_has_privileges(handle))
@@ -925,7 +933,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- info = kmalloc(sizeof *info, GFP_KERNEL);
+ info = kzalloc(sizeof *info, GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
@@ -952,7 +960,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- map = kmalloc(sizeof *map, GFP_KERNEL);
+ map = kzalloc(sizeof *map, GFP_KERNEL);
if (map == NULL)
return -ENOMEM;
@@ -979,7 +987,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
default:
if ((ret = v4l_compat_translate_ioctl(file, cmd, arg,
- __uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD)
+ uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD)
uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n",
cmd);
return ret;
@@ -988,17 +996,16 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
return ret;
}
-static int uvc_v4l2_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
-{
- return __uvc_v4l2_do_ioctl(file, cmd, arg);
-}
-
static int uvc_v4l2_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_ioctl\n");
- return video_usercopy(inode, file, cmd, arg, uvc_v4l2_do_ioctl);
+ if (uvc_trace_param & UVC_TRACE_IOCTL) {
+ uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl(");
+ v4l_printk_ioctl(cmd);
+ printk(")\n");
+ }
+
+ return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
}
static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index b7bb23820d80..e7c31995527f 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -36,15 +36,22 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
{
__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
unsigned int pipe;
- int ret;
pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
: usb_sndctrlpipe(dev->udev, 0);
type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
- ret = usb_control_msg(dev->udev, pipe, query, type, cs << 8,
+ return usb_control_msg(dev->udev, pipe, query, type, cs << 8,
unit << 8 | intfnum, data, size, timeout);
+}
+
+int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+ __u8 intfnum, __u8 cs, void *data, __u16 size)
+{
+ int ret;
+ ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
+ UVC_CTRL_CONTROL_TIMEOUT);
if (ret != size) {
uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u "
"(unit %u) : %d (exp. %u).\n", query, cs, unit, ret,
@@ -55,13 +62,6 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
return 0;
}
-int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
- __u8 intfnum, __u8 cs, void *data, __u16 size)
-{
- return __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
- UVC_CTRL_CONTROL_TIMEOUT);
-}
-
static void uvc_fixup_buffer_size(struct uvc_video_device *video,
struct uvc_streaming_control *ctrl)
{
@@ -102,8 +102,36 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum,
probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
UVC_CTRL_STREAMING_TIMEOUT);
- if (ret < 0)
+
+ if ((query == GET_MIN || query == GET_MAX) && ret == 2) {
+ /* Some cameras, mostly based on Bison Electronics chipsets,
+ * answer a GET_MIN or GET_MAX request with the wCompQuality
+ * field only.
+ */
+ uvc_warn_once(video->dev, UVC_WARN_MINMAX, "UVC non "
+ "compliance - GET_MIN/MAX(PROBE) incorrectly "
+ "supported. Enabling workaround.\n");
+ memset(ctrl, 0, sizeof ctrl);
+ ctrl->wCompQuality = le16_to_cpup((__le16 *)data);
+ ret = 0;
+ goto out;
+ } else if (query == GET_DEF && probe == 1) {
+ /* Many cameras don't support the GET_DEF request on their
+ * video probe control. Warn once and return, the caller will
+ * fall back to GET_CUR.
+ */
+ uvc_warn_once(video->dev, UVC_WARN_PROBE_DEF, "UVC non "
+ "compliance - GET_DEF(PROBE) not supported. "
+ "Enabling workaround.\n");
+ ret = -EIO;
+ goto out;
+ } else if (ret != size) {
+ uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : "
+ "%d (exp. %u).\n", query, probe ? "probe" : "commit",
+ ret, size);
+ ret = -EIO;
goto out;
+ }
ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]);
ctrl->bFormatIndex = data[2];
@@ -114,14 +142,11 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]);
ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]);
ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]);
- ctrl->dwMaxVideoFrameSize =
- le32_to_cpu(get_unaligned((__le32 *)&data[18]));
- ctrl->dwMaxPayloadTransferSize =
- le32_to_cpu(get_unaligned((__le32 *)&data[22]));
+ ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]);
+ ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]);
if (size == 34) {
- ctrl->dwClockFrequency =
- le32_to_cpu(get_unaligned((__le32 *)&data[26]));
+ ctrl->dwClockFrequency = get_unaligned_le32(&data[26]);
ctrl->bmFramingInfo = data[30];
ctrl->bPreferedVersion = data[31];
ctrl->bMinVersion = data[32];
@@ -138,13 +163,14 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
* Try to get the value from the format and frame descriptor.
*/
uvc_fixup_buffer_size(video, ctrl);
+ ret = 0;
out:
kfree(data);
return ret;
}
-int uvc_set_video_ctrl(struct uvc_video_device *video,
+static int uvc_set_video_ctrl(struct uvc_video_device *video,
struct uvc_streaming_control *ctrl, int probe)
{
__u8 *data;
@@ -168,14 +194,11 @@ int uvc_set_video_ctrl(struct uvc_video_device *video,
/* Note: Some of the fields below are not required for IN devices (see
* UVC spec, 4.3.1.1), but we still copy them in case support for OUT
* devices is added in the future. */
- put_unaligned(cpu_to_le32(ctrl->dwMaxVideoFrameSize),
- (__le32 *)&data[18]);
- put_unaligned(cpu_to_le32(ctrl->dwMaxPayloadTransferSize),
- (__le32 *)&data[22]);
+ put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
+ put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
if (size == 34) {
- put_unaligned(cpu_to_le32(ctrl->dwClockFrequency),
- (__le32 *)&data[26]);
+ put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
data[30] = ctrl->bmFramingInfo;
data[31] = ctrl->bPreferedVersion;
data[32] = ctrl->bMinVersion;
@@ -186,6 +209,12 @@ int uvc_set_video_ctrl(struct uvc_video_device *video,
video->streaming->intfnum,
probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
UVC_CTRL_STREAMING_TIMEOUT);
+ if (ret != size) {
+ uvc_printk(KERN_ERR, "Failed to set UVC %s control : "
+ "%d (exp. %u).\n", probe ? "probe" : "commit",
+ ret, size);
+ ret = -EIO;
+ }
kfree(data);
return ret;
@@ -252,6 +281,12 @@ done:
return ret;
}
+int uvc_commit_video(struct uvc_video_device *video,
+ struct uvc_streaming_control *probe)
+{
+ return uvc_set_video_ctrl(video, probe, 0);
+}
+
/* ------------------------------------------------------------------------
* Video codecs
*/
@@ -333,7 +368,7 @@ static int uvc_video_decode_start(struct uvc_video_device *video,
/* Synchronize to the input stream by waiting for the FID bit to be
* toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE.
- * queue->last_fid is initialized to -1, so the first isochronous
+ * video->last_fid is initialized to -1, so the first isochronous
* frame will always be in sync.
*
* If the device doesn't toggle the FID bit, invert video->last_fid
@@ -360,7 +395,7 @@ static int uvc_video_decode_start(struct uvc_video_device *video,
* last payload can be lost anyway). We thus must check if the FID has
* been toggled.
*
- * queue->last_fid is initialized to -1, so the first isochronous
+ * video->last_fid is initialized to -1, so the first isochronous
* frame will never trigger an end of frame detection.
*
* Empty buffers (bytesused == 0) don't trigger end of frame detection
@@ -418,6 +453,34 @@ static void uvc_video_decode_end(struct uvc_video_device *video,
}
}
+static int uvc_video_encode_header(struct uvc_video_device *video,
+ struct uvc_buffer *buf, __u8 *data, int len)
+{
+ data[0] = 2; /* Header length */
+ data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF
+ | (video->last_fid & UVC_STREAM_FID);
+ return 2;
+}
+
+static int uvc_video_encode_data(struct uvc_video_device *video,
+ struct uvc_buffer *buf, __u8 *data, int len)
+{
+ struct uvc_video_queue *queue = &video->queue;
+ unsigned int nbytes;
+ void *mem;
+
+ /* Copy video data to the URB buffer. */
+ mem = queue->mem + buf->buf.m.offset + queue->buf_used;
+ nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used);
+ nbytes = min(video->bulk.max_payload_size - video->bulk.payload_size,
+ nbytes);
+ memcpy(data, mem, nbytes);
+
+ queue->buf_used += nbytes;
+
+ return nbytes;
+}
+
/* ------------------------------------------------------------------------
* URB handling
*/
@@ -477,7 +540,7 @@ static void uvc_video_decode_bulk(struct urb *urb,
/* If the URB is the first of its payload, decode and save the
* header.
*/
- if (video->bulk.header_size == 0) {
+ if (video->bulk.header_size == 0 && !video->bulk.skip_payload) {
do {
ret = uvc_video_decode_start(video, buf, mem, len);
if (ret == -EAGAIN)
@@ -487,14 +550,13 @@ static void uvc_video_decode_bulk(struct urb *urb,
/* If an error occured skip the rest of the payload. */
if (ret < 0 || buf == NULL) {
video->bulk.skip_payload = 1;
- return;
- }
+ } else {
+ memcpy(video->bulk.header, mem, ret);
+ video->bulk.header_size = ret;
- video->bulk.header_size = ret;
- memcpy(video->bulk.header, mem, video->bulk.header_size);
-
- mem += ret;
- len -= ret;
+ mem += ret;
+ len -= ret;
+ }
}
/* The buffer queue might have been cancelled while a bulk transfer
@@ -525,6 +587,48 @@ static void uvc_video_decode_bulk(struct urb *urb,
}
}
+static void uvc_video_encode_bulk(struct urb *urb,
+ struct uvc_video_device *video, struct uvc_buffer *buf)
+{
+ u8 *mem = urb->transfer_buffer;
+ int len = video->urb_size, ret;
+
+ if (buf == NULL) {
+ urb->transfer_buffer_length = 0;
+ return;
+ }
+
+ /* If the URB is the first of its payload, add the header. */
+ if (video->bulk.header_size == 0) {
+ ret = uvc_video_encode_header(video, buf, mem, len);
+ video->bulk.header_size = ret;
+ video->bulk.payload_size += ret;
+ mem += ret;
+ len -= ret;
+ }
+
+ /* Process video data. */
+ ret = uvc_video_encode_data(video, buf, mem, len);
+
+ video->bulk.payload_size += ret;
+ len -= ret;
+
+ if (buf->buf.bytesused == video->queue.buf_used ||
+ video->bulk.payload_size == video->bulk.max_payload_size) {
+ if (buf->buf.bytesused == video->queue.buf_used) {
+ video->queue.buf_used = 0;
+ buf->state = UVC_BUF_STATE_DONE;
+ uvc_queue_next_buffer(&video->queue, buf);
+ video->last_fid ^= UVC_STREAM_FID;
+ }
+
+ video->bulk.header_size = 0;
+ video->bulk.payload_size = 0;
+ }
+
+ urb->transfer_buffer_length = video->urb_size - len;
+}
+
static void uvc_video_complete(struct urb *urb)
{
struct uvc_video_device *video = urb->context;
@@ -722,7 +826,15 @@ static int uvc_init_video_bulk(struct uvc_video_device *video,
if (uvc_alloc_urb_buffers(video, size) < 0)
return -ENOMEM;
- pipe = usb_rcvbulkpipe(video->dev->udev, ep->desc.bEndpointAddress);
+ if (usb_endpoint_dir_in(&ep->desc))
+ pipe = usb_rcvbulkpipe(video->dev->udev,
+ ep->desc.bEndpointAddress);
+ else
+ pipe = usb_sndbulkpipe(video->dev->udev,
+ ep->desc.bEndpointAddress);
+
+ if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ size = 0;
for (i = 0; i < UVC_URBS; ++i) {
urb = usb_alloc_urb(0, gfp_flags);
@@ -854,7 +966,7 @@ int uvc_video_resume(struct uvc_video_device *video)
video->frozen = 0;
- if ((ret = uvc_set_video_ctrl(video, &video->streaming->ctrl, 0)) < 0) {
+ if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) {
uvc_queue_enable(&video->queue, 0);
return ret;
}
@@ -935,23 +1047,30 @@ int uvc_video_init(struct uvc_video_device *video)
break;
}
- /* Commit the default settings. */
probe->bFormatIndex = format->index;
probe->bFrameIndex = frame->bFrameIndex;
- if ((ret = uvc_set_video_ctrl(video, probe, 0)) < 0)
- return ret;
video->streaming->cur_format = format;
video->streaming->cur_frame = frame;
atomic_set(&video->active, 0);
/* Select the video decoding function */
- if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
- video->decode = uvc_video_decode_isight;
- else if (video->streaming->intf->num_altsetting > 1)
- video->decode = uvc_video_decode_isoc;
- else
- video->decode = uvc_video_decode_bulk;
+ if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
+ video->decode = uvc_video_decode_isight;
+ else if (video->streaming->intf->num_altsetting > 1)
+ video->decode = uvc_video_decode_isoc;
+ else
+ video->decode = uvc_video_decode_bulk;
+ } else {
+ if (video->streaming->intf->num_altsetting == 1)
+ video->decode = uvc_video_encode_bulk;
+ else {
+ uvc_printk(KERN_INFO, "Isochronous endpoints are not "
+ "supported for video output devices.\n");
+ return -EINVAL;
+ }
+ }
return 0;
}
@@ -971,7 +1090,8 @@ int uvc_video_enable(struct uvc_video_device *video, int enable)
return 0;
}
- if (video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED)
+ if ((video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) ||
+ uvc_no_drop_param)
video->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
else
video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
@@ -979,6 +1099,10 @@ int uvc_video_enable(struct uvc_video_device *video, int enable)
if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
return ret;
+ /* Commit the streaming parameters. */
+ if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0)
+ return ret;
+
return uvc_init_video(video, GFP_KERNEL);
}
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index 9a6bc1aafb16..896b791ece15 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -4,7 +4,6 @@
#include <linux/kernel.h>
#include <linux/videodev2.h>
-
/*
* Dynamic controls
*/
@@ -316,6 +315,7 @@ struct uvc_xu_control {
#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008
#define UVC_QUIRK_STREAM_NO_FID 0x00000010
#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020
+#define UVC_QUIRK_PRUNE_CONTROLS 0x00000040
/* Format flags */
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
@@ -383,6 +383,11 @@ struct uvc_control_mapping {
struct uvc_menu_info *menu_info;
__u32 menu_count;
+
+ __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query,
+ const __u8 *data);
+ void (*set) (struct uvc_control_mapping *mapping, __s32 value,
+ __u8 *data);
};
struct uvc_control {
@@ -523,6 +528,7 @@ struct uvc_streaming {
__u16 maxpsize;
struct uvc_streaming_header header;
+ enum v4l2_buf_type type;
unsigned int nformats;
struct uvc_format *format;
@@ -558,12 +564,15 @@ struct uvc_buffer {
#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2)
struct uvc_video_queue {
+ enum v4l2_buf_type type;
+
void *mem;
unsigned int flags;
__u32 sequence;
unsigned int count;
unsigned int buf_size;
+ unsigned int buf_used;
struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS];
struct mutex mutex; /* protects buffers and mainqueue */
spinlock_t irqlock; /* protects irqqueue */
@@ -578,8 +587,9 @@ struct uvc_video_device {
atomic_t active;
unsigned int frozen : 1;
- struct list_head iterms;
- struct uvc_entity *oterm;
+ struct list_head iterms; /* Input terminals */
+ struct uvc_entity *oterm; /* Output terminal */
+ struct uvc_entity *sterm; /* USB streaming terminal */
struct uvc_entity *processing;
struct uvc_entity *selector;
struct list_head extensions;
@@ -617,6 +627,7 @@ enum uvc_device_state {
struct uvc_device {
struct usb_device *udev;
struct usb_interface *intf;
+ unsigned long warnings;
__u32 quirks;
int intfnum;
char name[32];
@@ -679,6 +690,10 @@ struct uvc_driver {
#define UVC_TRACE_SUSPEND (1 << 8)
#define UVC_TRACE_STATUS (1 << 9)
+#define UVC_WARN_MINMAX 0
+#define UVC_WARN_PROBE_DEF 1
+
+extern unsigned int uvc_no_drop_param;
extern unsigned int uvc_trace_param;
#define uvc_trace(flag, msg...) \
@@ -687,6 +702,12 @@ extern unsigned int uvc_trace_param;
printk(KERN_DEBUG "uvcvideo: " msg); \
} while (0)
+#define uvc_warn_once(dev, warn, msg...) \
+ do { \
+ if (!test_and_set_bit(warn, &dev->warnings)) \
+ printk(KERN_INFO "uvcvideo: " msg); \
+ } while (0)
+
#define uvc_printk(level, msg...) \
printk(level "uvcvideo: " msg)
@@ -709,7 +730,8 @@ extern struct uvc_driver uvc_driver;
extern void uvc_delete(struct kref *kref);
/* Video buffers queue management. */
-extern void uvc_queue_init(struct uvc_video_queue *queue);
+extern void uvc_queue_init(struct uvc_video_queue *queue,
+ enum v4l2_buf_type type);
extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
unsigned int nbuffers, unsigned int buflength);
extern int uvc_free_buffers(struct uvc_video_queue *queue);
@@ -740,10 +762,10 @@ extern int uvc_video_resume(struct uvc_video_device *video);
extern int uvc_video_enable(struct uvc_video_device *video, int enable);
extern int uvc_probe_video(struct uvc_video_device *video,
struct uvc_streaming_control *probe);
+extern int uvc_commit_video(struct uvc_video_device *video,
+ struct uvc_streaming_control *ctrl);
extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
__u8 intfnum, __u8 cs, void *data, __u16 size);
-extern int uvc_set_video_ctrl(struct uvc_video_device *video,
- struct uvc_streaming_control *ctrl, int probe);
/* Status */
extern int uvc_status_init(struct uvc_device *dev);
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 846763d7349e..c676b0b0f708 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -28,7 +28,7 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Author: Alan Cox, <alan@redhat.com>
+ * Author: Alan Cox, <alan@lxorguk.ukuu.org.uk>
*
* Fixes:
*/
@@ -58,6 +58,7 @@
#include <asm/div64.h>
#define __OLD_VIDIOC_ /* To allow fixing old calls*/
#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <linux/videodev2.h>
@@ -320,6 +321,19 @@ const char **v4l2_ctrl_get_menu(u32 id)
"Private packet, IVTV format",
NULL
};
+ static const char *camera_power_line_frequency[] = {
+ "Disabled",
+ "50 Hz",
+ "60 Hz",
+ NULL
+ };
+ static const char *camera_exposure_auto[] = {
+ "Auto Mode",
+ "Manual Mode",
+ "Shutter Priority Mode",
+ "Aperture Priority Mode",
+ NULL
+ };
switch (id) {
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
@@ -352,6 +366,10 @@ const char **v4l2_ctrl_get_menu(u32 id)
return mpeg_stream_type;
case V4L2_CID_MPEG_STREAM_VBI_FMT:
return mpeg_stream_vbi_fmt;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ return camera_power_line_frequency;
+ case V4L2_CID_EXPOSURE_AUTO:
+ return camera_exposure_auto;
default:
return NULL;
}
@@ -363,17 +381,37 @@ const char *v4l2_ctrl_get_name(u32 id)
{
switch (id) {
/* USER controls */
- case V4L2_CID_USER_CLASS: return "User Controls";
- case V4L2_CID_AUDIO_VOLUME: return "Volume";
- case V4L2_CID_AUDIO_MUTE: return "Mute";
- case V4L2_CID_AUDIO_BALANCE: return "Balance";
- case V4L2_CID_AUDIO_BASS: return "Bass";
- case V4L2_CID_AUDIO_TREBLE: return "Treble";
- case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
- case V4L2_CID_BRIGHTNESS: return "Brightness";
- case V4L2_CID_CONTRAST: return "Contrast";
- case V4L2_CID_SATURATION: return "Saturation";
- case V4L2_CID_HUE: return "Hue";
+ case V4L2_CID_USER_CLASS: return "User Controls";
+ case V4L2_CID_AUDIO_VOLUME: return "Volume";
+ case V4L2_CID_AUDIO_MUTE: return "Mute";
+ case V4L2_CID_AUDIO_BALANCE: return "Balance";
+ case V4L2_CID_AUDIO_BASS: return "Bass";
+ case V4L2_CID_AUDIO_TREBLE: return "Treble";
+ case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
+ case V4L2_CID_BRIGHTNESS: return "Brightness";
+ case V4L2_CID_CONTRAST: return "Contrast";
+ case V4L2_CID_SATURATION: return "Saturation";
+ case V4L2_CID_HUE: return "Hue";
+ case V4L2_CID_BLACK_LEVEL: return "Black Level";
+ case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
+ case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
+ case V4L2_CID_RED_BALANCE: return "Red Balance";
+ case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
+ case V4L2_CID_GAMMA: return "Gamma";
+ case V4L2_CID_EXPOSURE: return "Exposure";
+ case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
+ case V4L2_CID_GAIN: return "Gain";
+ case V4L2_CID_HFLIP: return "Horizontal Flip";
+ case V4L2_CID_VFLIP: return "Vertical Flip";
+ case V4L2_CID_HCENTER: return "Horizontal Center";
+ case V4L2_CID_VCENTER: return "Vertical Center";
+ case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
+ case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
+ case V4L2_CID_SHARPNESS: return "Sharpness";
+ case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation";
+ case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
+ case V4L2_CID_COLOR_KILLER: return "Color Killer";
/* MPEG controls */
case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
@@ -410,6 +448,25 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format";
+ /* CAMERA controls */
+ case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
+ case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure";
+ case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute";
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
+ case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
+ case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
+ case V4L2_CID_PAN_RESET: return "Pan, Reset";
+ case V4L2_CID_TILT_RESET: return "Tilt, Reset";
+ case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
+ case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
+ case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute";
+ case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative";
+ case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic";
+ case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute";
+ case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative";
+ case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous";
+ case V4L2_CID_PRIVACY: return "Privacy";
+
default:
return NULL;
}
@@ -428,14 +485,22 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
switch (qctrl->id) {
case V4L2_CID_AUDIO_MUTE:
case V4L2_CID_AUDIO_LOUDNESS:
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ case V4L2_CID_AUTOGAIN:
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HUE_AUTO:
case V4L2_CID_MPEG_AUDIO_MUTE:
case V4L2_CID_MPEG_VIDEO_MUTE:
case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
case V4L2_CID_MPEG_VIDEO_PULLDOWN:
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
+ case V4L2_CID_PRIVACY:
qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
min = 0;
max = step = 1;
break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
case V4L2_CID_MPEG_AUDIO_ENCODING:
case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
@@ -451,10 +516,12 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
case V4L2_CID_MPEG_STREAM_TYPE:
case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ case V4L2_CID_EXPOSURE_AUTO:
qctrl->type = V4L2_CTRL_TYPE_MENU;
step = 1;
break;
case V4L2_CID_USER_CLASS:
+ case V4L2_CID_CAMERA_CLASS:
case V4L2_CID_MPEG_CLASS:
qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS;
qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
@@ -801,4 +868,116 @@ int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver
return err != -ENOMEM ? 0 : err;
}
EXPORT_SYMBOL(v4l2_i2c_attach);
+
+void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
+ const struct v4l2_subdev_ops *ops)
+{
+ v4l2_subdev_init(sd, ops);
+ /* the owner is the same as the i2c_client's driver owner */
+ sd->owner = client->driver->driver.owner;
+ /* i2c_client and v4l2_subdev point to one another */
+ v4l2_set_subdevdata(sd, client);
+ i2c_set_clientdata(client, sd);
+ /* initialize name */
+ snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
+ client->driver->driver.name, i2c_adapter_id(client->adapter),
+ client->addr);
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init);
+
+
+
+/* Load an i2c sub-device. It assumes that i2c_get_adapdata(adapter)
+ returns the v4l2_device and that i2c_get_clientdata(client)
+ returns the v4l2_subdev. */
+struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter,
+ const char *module_name, const char *client_type, u8 addr)
+{
+ struct v4l2_device *dev = i2c_get_adapdata(adapter);
+ struct v4l2_subdev *sd = NULL;
+ struct i2c_client *client;
+ struct i2c_board_info info;
+
+ BUG_ON(!dev);
+#ifdef MODULE
+ if (module_name)
+ request_module(module_name);
+#endif
+ /* Setup the i2c board info with the device type and
+ the device address. */
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.type, client_type, sizeof(info.type));
+ info.addr = addr;
+
+ /* Create the i2c client */
+ client = i2c_new_device(adapter, &info);
+ /* Note: it is possible in the future that
+ c->driver is NULL if the driver is still being loaded.
+ We need better support from the kernel so that we
+ can easily wait for the load to finish. */
+ if (client == NULL || client->driver == NULL)
+ return NULL;
+
+ /* Lock the module so we can safely get the v4l2_subdev pointer */
+ if (!try_module_get(client->driver->driver.owner))
+ return NULL;
+ sd = i2c_get_clientdata(client);
+
+ /* Register with the v4l2_device which increases the module's
+ use count as well. */
+ if (v4l2_device_register_subdev(dev, sd))
+ sd = NULL;
+ /* Decrease the module use count to match the first try_module_get. */
+ module_put(client->driver->driver.owner);
+ return sd;
+
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev);
+
+/* Probe and load an i2c sub-device. It assumes that i2c_get_adapdata(adapter)
+ returns the v4l2_device and that i2c_get_clientdata(client)
+ returns the v4l2_subdev. */
+struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter,
+ const char *module_name, const char *client_type,
+ const unsigned short *addrs)
+{
+ struct v4l2_device *dev = i2c_get_adapdata(adapter);
+ struct v4l2_subdev *sd = NULL;
+ struct i2c_client *client = NULL;
+ struct i2c_board_info info;
+
+ BUG_ON(!dev);
+#ifdef MODULE
+ if (module_name)
+ request_module(module_name);
+#endif
+ /* Setup the i2c board info with the device type and
+ the device address. */
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.type, client_type, sizeof(info.type));
+
+ /* Probe and create the i2c client */
+ client = i2c_new_probed_device(adapter, &info, addrs);
+ /* Note: it is possible in the future that
+ c->driver is NULL if the driver is still being loaded.
+ We need better support from the kernel so that we
+ can easily wait for the load to finish. */
+ if (client == NULL || client->driver == NULL)
+ return NULL;
+
+ /* Lock the module so we can safely get the v4l2_subdev pointer */
+ if (!try_module_get(client->driver->driver.owner))
+ return NULL;
+ sd = i2c_get_clientdata(client);
+
+ /* Register with the v4l2_device which increases the module's
+ use count as well. */
+ if (v4l2_device_register_subdev(dev, sd))
+ sd = NULL;
+ /* Decrease the module use count to match the first try_module_get. */
+ module_put(client->driver->driver.owner);
+ return sd;
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev);
+
#endif
diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
index 0ea85a05e5c0..d0e1bd3ace6a 100644
--- a/drivers/media/video/compat_ioctl32.c
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -7,12 +7,14 @@
* Copyright (C) 2001,2002 Andi Kleen, SuSE Labs
* Copyright (C) 2003 Pavel Machek (pavel@suse.cz)
* Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be)
+ * Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
*
* These routines maintain argument size conversion between 32bit and 64bit
* ioctls.
*/
#include <linux/compat.h>
+#define __OLD_VIDIOC_ /* To allow fixing old calls*/
#include <linux/videodev.h>
#include <linux/videodev2.h>
#include <linux/module.h>
@@ -32,7 +34,7 @@ struct video_tuner32 {
static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user *up)
{
- if(!access_ok(VERIFY_READ, up, sizeof(struct video_tuner32)) ||
+ if (!access_ok(VERIFY_READ, up, sizeof(struct video_tuner32)) ||
get_user(kp->tuner, &up->tuner) ||
copy_from_user(kp->name, up->name, 32) ||
get_user(kp->rangelow, &up->rangelow) ||
@@ -46,7 +48,7 @@ static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user
static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user *up)
{
- if(!access_ok(VERIFY_WRITE, up, sizeof(struct video_tuner32)) ||
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct video_tuner32)) ||
put_user(kp->tuner, &up->tuner) ||
copy_to_user(up->name, kp->name, 32) ||
put_user(kp->rangelow, &up->rangelow) ||
@@ -58,7 +60,6 @@ static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user
return 0;
}
-
struct video_buffer32 {
compat_caddr_t base;
compat_int_t height, width, depth, bytesperline;
@@ -88,7 +89,7 @@ static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 __u
{
u32 tmp = (u32)((unsigned long)kp->base);
- if(!access_ok(VERIFY_WRITE, up, sizeof(struct video_buffer32)) ||
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct video_buffer32)) ||
put_user(tmp, &up->base) ||
put_user(kp->height, &up->height) ||
put_user(kp->width, &up->width) ||
@@ -99,7 +100,7 @@ static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 __u
}
struct video_clip32 {
- s32 x, y, width, height; /* Its really s32 in videodev.h */
+ s32 x, y, width, height; /* It's really s32 in videodev.h */
compat_caddr_t next;
};
@@ -108,29 +109,76 @@ struct video_window32 {
compat_caddr_t clips;
compat_int_t clipcount;
};
-#endif
-static int native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static int get_video_window32(struct video_window *kp, struct video_window32 __user *up)
{
- int ret = -ENOIOCTLCMD;
+ struct video_clip __user *uclips;
+ struct video_clip __user *kclips;
+ compat_caddr_t p;
+ int nclips;
- if (file->f_op->unlocked_ioctl)
- ret = file->f_op->unlocked_ioctl(file, cmd, arg);
- else if (file->f_op->ioctl) {
- lock_kernel();
- ret = file->f_op->ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
- unlock_kernel();
+ if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32)))
+ return -EFAULT;
+
+ if (get_user(nclips, &up->clipcount))
+ return -EFAULT;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32)) ||
+ get_user(kp->x, &up->x) ||
+ get_user(kp->y, &up->y) ||
+ get_user(kp->width, &up->width) ||
+ get_user(kp->height, &up->height) ||
+ get_user(kp->chromakey, &up->chromakey) ||
+ get_user(kp->flags, &up->flags) ||
+ get_user(kp->clipcount, &up->clipcount))
+ return -EFAULT;
+
+ nclips = kp->clipcount;
+ kp->clips = NULL;
+
+ if (nclips == 0)
+ return 0;
+ if (get_user(p, &up->clips))
+ return -EFAULT;
+ uclips = compat_ptr(p);
+
+ /* If nclips < 0, then it is a clipping bitmap of size
+ VIDEO_CLIPMAP_SIZE */
+ if (nclips < 0) {
+ if (!access_ok(VERIFY_READ, uclips, VIDEO_CLIPMAP_SIZE))
+ return -EFAULT;
+ kp->clips = compat_alloc_user_space(VIDEO_CLIPMAP_SIZE);
+ if (copy_in_user(kp->clips, uclips, VIDEO_CLIPMAP_SIZE))
+ return -EFAULT;
+ return 0;
}
- return ret;
-}
+ /* Otherwise it is an array of video_clip structs. */
+ if (!access_ok(VERIFY_READ, uclips, nclips * sizeof(struct video_clip)))
+ return -EFAULT;
+ kp->clips = compat_alloc_user_space(nclips * sizeof(struct video_clip));
+ kclips = kp->clips;
+ while (nclips--) {
+ int err;
+
+ err = copy_in_user(&kclips->x, &uclips->x, sizeof(kclips->x));
+ err |= copy_in_user(&kclips->y, &uclips->y, sizeof(kclips->y));
+ err |= copy_in_user(&kclips->width, &uclips->width, sizeof(kclips->width));
+ err |= copy_in_user(&kclips->height, &uclips->height, sizeof(kclips->height));
+ kclips->next = NULL;
+ if (err)
+ return -EFAULT;
+ kclips++;
+ uclips++;
+ }
+ return 0;
+}
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
/* You get back everything except the clips... */
static int put_video_window32(struct video_window *kp, struct video_window32 __user *up)
{
- if(!access_ok(VERIFY_WRITE, up, sizeof(struct video_window32)) ||
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct video_window32)) ||
put_user(kp->x, &up->x) ||
put_user(kp->y, &up->y) ||
put_user(kp->width, &up->width) ||
@@ -141,16 +189,61 @@ static int put_video_window32(struct video_window *kp, struct video_window32 __u
return -EFAULT;
return 0;
}
+
+struct video_code32 {
+ char loadwhat[16]; /* name or tag of file being passed */
+ compat_int_t datasize;
+ unsigned char *data;
+};
+
+static int get_microcode32(struct video_code *kp, struct video_code32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct video_code32)) ||
+ copy_from_user(kp->loadwhat, up->loadwhat, sizeof(up->loadwhat)) ||
+ get_user(kp->datasize, &up->datasize) ||
+ copy_from_user(kp->data, up->data, up->datasize))
+ return -EFAULT;
+ return 0;
+}
+
+#define VIDIOCGTUNER32 _IOWR('v', 4, struct video_tuner32)
+#define VIDIOCSTUNER32 _IOW('v', 5, struct video_tuner32)
+#define VIDIOCGWIN32 _IOR('v', 9, struct video_window32)
+#define VIDIOCSWIN32 _IOW('v', 10, struct video_window32)
+#define VIDIOCGFBUF32 _IOR('v', 11, struct video_buffer32)
+#define VIDIOCSFBUF32 _IOW('v', 12, struct video_buffer32)
+#define VIDIOCGFREQ32 _IOR('v', 14, u32)
+#define VIDIOCSFREQ32 _IOW('v', 15, u32)
+#define VIDIOCSMICROCODE32 _IOW('v', 27, struct video_code32)
+
+#define VIDIOCCAPTURE32 _IOW('v', 8, s32)
+#define VIDIOCSYNC32 _IOW('v', 18, s32)
+#define VIDIOCSWRITEMODE32 _IOW('v', 25, s32)
+
#endif
-struct v4l2_clip32
+static int native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
+ int ret = -ENOIOCTLCMD;
+
+ if (file->f_op->unlocked_ioctl)
+ ret = file->f_op->unlocked_ioctl(file, cmd, arg);
+ else if (file->f_op->ioctl) {
+ lock_kernel();
+ ret = file->f_op->ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
+ unlock_kernel();
+ }
+
+ return ret;
+}
+
+
+struct v4l2_clip32 {
struct v4l2_rect c;
compat_caddr_t next;
};
-struct v4l2_window32
-{
+struct v4l2_window32 {
struct v4l2_rect w;
enum v4l2_field field;
__u32 chromakey;
@@ -231,15 +324,28 @@ static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vb
return 0;
}
-struct v4l2_format32
+static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+struct v4l2_format32 {
enum v4l2_buf_type type;
- union
- {
- struct v4l2_pix_format pix; // V4L2_BUF_TYPE_VIDEO_CAPTURE
- struct v4l2_window32 win; // V4L2_BUF_TYPE_VIDEO_OVERLAY
- struct v4l2_vbi_format vbi; // V4L2_BUF_TYPE_VBI_CAPTURE
- __u8 raw_data[200]; // user-defined
+ union {
+ struct v4l2_pix_format pix;
+ struct v4l2_window32 win;
+ struct v4l2_vbi_format vbi;
+ struct v4l2_sliced_vbi_format sliced;
+ __u8 raw_data[200]; /* user-defined */
} fmt;
};
@@ -250,52 +356,62 @@ static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
return -EFAULT;
switch (kp->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
return get_v4l2_window32(&kp->fmt.win, &up->fmt.win);
case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (copy_from_user(kp, up, sizeof(kp->fmt.raw_data)))
+ return -EFAULT;
+ return 0;
+ case 0:
+ return -EINVAL;
default:
- printk("compat_ioctl : unexpected VIDIOC_FMT type %d\n",
+ printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
kp->type);
- return -ENXIO;
+ return -EINVAL;
}
}
static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
{
- if(!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
put_user(kp->type, &up->type))
return -EFAULT;
switch (kp->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (copy_to_user(up, kp, sizeof(up->fmt.raw_data)))
+ return -EFAULT;
+ return 0;
+ case 0:
+ return -EINVAL;
default:
- return -ENXIO;
+ printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
+ kp->type);
+ return -EINVAL;
}
}
-static inline int get_v4l2_standard(struct v4l2_standard *kp, struct v4l2_standard __user *up)
-{
- if (copy_from_user(kp, up, sizeof(struct v4l2_standard)))
- return -EFAULT;
- return 0;
-
-}
-
-static inline int put_v4l2_standard(struct v4l2_standard *kp, struct v4l2_standard __user *up)
-{
- if (copy_to_user(up, kp, sizeof(struct v4l2_standard)))
- return -EFAULT;
- return 0;
-}
-
-struct v4l2_standard32
-{
+struct v4l2_standard32 {
__u32 index;
__u32 id[2]; /* __u64 would get the alignment wrong */
__u8 name[24];
@@ -315,7 +431,7 @@ static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32
static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
{
- if(!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
put_user(kp->index, &up->index) ||
copy_to_user(up->id, &kp->id, sizeof(__u64)) ||
copy_to_user(up->name, kp->name, 24) ||
@@ -326,23 +442,7 @@ static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32
return 0;
}
-static inline int get_v4l2_tuner(struct v4l2_tuner *kp, struct v4l2_tuner __user *up)
-{
- if (copy_from_user(kp, up, sizeof(struct v4l2_tuner)))
- return -EFAULT;
- return 0;
-
-}
-
-static inline int put_v4l2_tuner(struct v4l2_tuner *kp, struct v4l2_tuner __user *up)
-{
- if (copy_to_user(up, kp, sizeof(struct v4l2_tuner)))
- return -EFAULT;
- return 0;
-}
-
-struct v4l2_buffer32
-{
+struct v4l2_buffer32 {
__u32 index;
enum v4l2_buf_type type;
__u32 bytesused;
@@ -373,7 +473,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
get_user(kp->memory, &up->memory) ||
get_user(kp->input, &up->input))
return -EFAULT;
- switch(kp->memory) {
+ switch (kp->memory) {
case V4L2_MEMORY_MMAP:
break;
case V4L2_MEMORY_USERPTR:
@@ -388,7 +488,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
}
break;
case V4L2_MEMORY_OVERLAY:
- if(get_user(kp->m.offset, &up->m.offset))
+ if (get_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
}
@@ -404,7 +504,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
put_user(kp->memory, &up->memory) ||
put_user(kp->input, &up->input))
return -EFAULT;
- switch(kp->memory) {
+ switch (kp->memory) {
case V4L2_MEMORY_MMAP:
if (put_user(kp->length, &up->length) ||
put_user(kp->m.offset, &up->m.offset))
@@ -431,8 +531,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
return 0;
}
-struct v4l2_framebuffer32
-{
+struct v4l2_framebuffer32 {
__u32 capability;
__u32 flags;
compat_caddr_t base;
@@ -457,7 +556,7 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame
{
u32 tmp = (u32)((unsigned long)kp->base);
- if(!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
put_user(tmp, &up->base) ||
put_user(kp->capability, &up->capability) ||
put_user(kp->flags, &up->flags))
@@ -466,150 +565,145 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame
return 0;
}
-static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input __user *up)
+struct v4l2_input32 {
+ __u32 index; /* Which input */
+ __u8 name[32]; /* Label */
+ __u32 type; /* Type of input */
+ __u32 audioset; /* Associated audios (bitfield) */
+ __u32 tuner; /* Associated tuner */
+ v4l2_std_id std;
+ __u32 status;
+ __u32 reserved[4];
+} __attribute__ ((packed));
+
+/* The 64-bit v4l2_input struct has extra padding at the end of the struct.
+ Otherwise it is identical to the 32-bit version. */
+static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
{
- if (copy_from_user(kp, up, sizeof(struct v4l2_input) - 4))
+ if (copy_from_user(kp, up, sizeof(struct v4l2_input32)))
return -EFAULT;
return 0;
}
-static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input __user *up)
+static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
{
- if (copy_to_user(up, kp, sizeof(struct v4l2_input) - 4))
+ if (copy_to_user(up, kp, sizeof(struct v4l2_input32)))
return -EFAULT;
return 0;
}
-static inline int get_v4l2_input(struct v4l2_input *kp, struct v4l2_input __user *up)
-{
- if (copy_from_user(kp, up, sizeof(struct v4l2_input)))
- return -EFAULT;
- return 0;
-}
-
-static inline int put_v4l2_input(struct v4l2_input *kp, struct v4l2_input __user *up)
-{
- if (copy_to_user(up, kp, sizeof(struct v4l2_input)))
- return -EFAULT;
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-struct video_code32
-{
- char loadwhat[16]; /* name or tag of file being passed */
- compat_int_t datasize;
- unsigned char *data;
+struct v4l2_ext_controls32 {
+ __u32 ctrl_class;
+ __u32 count;
+ __u32 error_idx;
+ __u32 reserved[2];
+ compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */
};
-static inline int microcode32(struct video_code *kp, struct video_code32 __user *up)
+static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
{
- if(!access_ok(VERIFY_READ, up, sizeof(struct video_code32)) ||
- copy_from_user(kp->loadwhat, up->loadwhat, sizeof (up->loadwhat)) ||
- get_user(kp->datasize, &up->datasize) ||
- copy_from_user(kp->data, up->data, up->datasize))
+ struct v4l2_ext_control __user *ucontrols;
+ struct v4l2_ext_control __user *kcontrols;
+ int n;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
+ get_user(kp->ctrl_class, &up->ctrl_class) ||
+ get_user(kp->count, &up->count) ||
+ get_user(kp->error_idx, &up->error_idx) ||
+ copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
return -EFAULT;
+ n = kp->count;
+ if (n == 0) {
+ kp->controls = NULL;
+ return 0;
+ }
+ if (get_user(p, &up->controls))
+ return -EFAULT;
+ ucontrols = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, ucontrols, n * sizeof(struct v4l2_ext_control)))
+ return -EFAULT;
+ kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control));
+ kp->controls = kcontrols;
+ while (--n >= 0) {
+ if (copy_in_user(&kcontrols->id, &ucontrols->id, sizeof(__u32)))
+ return -EFAULT;
+ if (copy_in_user(&kcontrols->reserved2, &ucontrols->reserved2, sizeof(ucontrols->reserved2)))
+ return -EFAULT;
+ /* Note: if the void * part of the union ever becomes relevant
+ then we need to know the type of the control in order to do
+ the right thing here. Luckily, that is not yet an issue. */
+ if (copy_in_user(&kcontrols->value, &ucontrols->value, sizeof(ucontrols->value)))
+ return -EFAULT;
+ ucontrols++;
+ kcontrols++;
+ }
return 0;
}
-#define VIDIOCGTUNER32 _IOWR('v',4, struct video_tuner32)
-#define VIDIOCSTUNER32 _IOW('v',5, struct video_tuner32)
-#define VIDIOCGWIN32 _IOR('v',9, struct video_window32)
-#define VIDIOCSWIN32 _IOW('v',10, struct video_window32)
-#define VIDIOCGFBUF32 _IOR('v',11, struct video_buffer32)
-#define VIDIOCSFBUF32 _IOW('v',12, struct video_buffer32)
-#define VIDIOCGFREQ32 _IOR('v',14, u32)
-#define VIDIOCSFREQ32 _IOW('v',15, u32)
-#define VIDIOCSMICROCODE32 _IOW('v',27, struct video_code32)
-
-#endif
-
-/* VIDIOC_ENUMINPUT32 is VIDIOC_ENUMINPUT minus 4 bytes of padding alignement */
-#define VIDIOC_ENUMINPUT32 VIDIOC_ENUMINPUT - _IOC(0, 0, 0, 4)
-#define VIDIOC_G_FMT32 _IOWR ('V', 4, struct v4l2_format32)
-#define VIDIOC_S_FMT32 _IOWR ('V', 5, struct v4l2_format32)
-#define VIDIOC_QUERYBUF32 _IOWR ('V', 9, struct v4l2_buffer32)
-#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32)
-#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32)
-/* VIDIOC_OVERLAY is now _IOW, but was _IOWR */
-#define VIDIOC_OVERLAY32 _IOWR ('V', 14, compat_int_t)
-#define VIDIOC_QBUF32 _IOWR ('V', 15, struct v4l2_buffer32)
-#define VIDIOC_DQBUF32 _IOWR ('V', 17, struct v4l2_buffer32)
-#define VIDIOC_STREAMON32 _IOW ('V', 18, compat_int_t)
-#define VIDIOC_STREAMOFF32 _IOW ('V', 19, compat_int_t)
-#define VIDIOC_ENUMSTD32 _IOWR ('V', 25, struct v4l2_standard32)
-/* VIDIOC_S_CTRL is now _IOWR, but was _IOW */
-#define VIDIOC_S_CTRL32 _IOW ('V', 28, struct v4l2_control)
-#define VIDIOC_G_INPUT32 _IOR ('V', 38, compat_int_t)
-#define VIDIOC_S_INPUT32 _IOWR ('V', 39, compat_int_t)
-#define VIDIOC_TRY_FMT32 _IOWR ('V', 64, struct v4l2_format32)
-
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-enum {
- MaxClips = (~0U-sizeof(struct video_window))/sizeof(struct video_clip)
-};
-
-static int do_set_window(struct file *file, unsigned int cmd, unsigned long arg)
+static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
{
- struct video_window32 __user *up = compat_ptr(arg);
- struct video_window __user *vw;
- struct video_clip __user *p;
- int nclips;
- u32 n;
-
- if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32)))
- return -EFAULT;
+ struct v4l2_ext_control __user *ucontrols;
+ struct v4l2_ext_control __user *kcontrols = kp->controls;
+ int n = kp->count;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
+ put_user(kp->ctrl_class, &up->ctrl_class) ||
+ put_user(kp->count, &up->count) ||
+ put_user(kp->error_idx, &up->error_idx) ||
+ copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
+ return -EFAULT;
+ if (!kp->count)
+ return 0;
- if (get_user(nclips, &up->clipcount))
+ if (get_user(p, &up->controls))
return -EFAULT;
-
- /* Peculiar interface... */
- if (nclips < 0)
- nclips = VIDEO_CLIPMAP_SIZE;
-
- if (nclips > MaxClips)
- return -ENOMEM;
-
- vw = compat_alloc_user_space(sizeof(struct video_window) +
- nclips * sizeof(struct video_clip));
-
- p = nclips ? (struct video_clip __user *)(vw + 1) : NULL;
-
- if (get_user(n, &up->x) || put_user(n, &vw->x) ||
- get_user(n, &up->y) || put_user(n, &vw->y) ||
- get_user(n, &up->width) || put_user(n, &vw->width) ||
- get_user(n, &up->height) || put_user(n, &vw->height) ||
- get_user(n, &up->chromakey) || put_user(n, &vw->chromakey) ||
- get_user(n, &up->flags) || put_user(n, &vw->flags) ||
- get_user(n, &up->clipcount) || put_user(n, &vw->clipcount) ||
- get_user(n, &up->clips) || put_user(p, &vw->clips))
+ ucontrols = compat_ptr(p);
+ if (!access_ok(VERIFY_WRITE, ucontrols, n * sizeof(struct v4l2_ext_control)))
return -EFAULT;
- if (nclips) {
- struct video_clip32 __user *u = compat_ptr(n);
- int i;
- if (!u)
- return -EINVAL;
- for (i = 0; i < nclips; i++, u++, p++) {
- s32 v;
- if (!access_ok(VERIFY_READ, u, sizeof(struct video_clip32)) ||
- !access_ok(VERIFY_WRITE, p, sizeof(struct video_clip32)) ||
- get_user(v, &u->x) ||
- put_user(v, &p->x) ||
- get_user(v, &u->y) ||
- put_user(v, &p->y) ||
- get_user(v, &u->width) ||
- put_user(v, &p->width) ||
- get_user(v, &u->height) ||
- put_user(v, &p->height) ||
- put_user(NULL, &p->next))
- return -EFAULT;
- }
+ while (--n >= 0) {
+ if (copy_in_user(&ucontrols->id, &kcontrols->id, sizeof(__u32)))
+ return -EFAULT;
+ if (copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2,
+ sizeof(ucontrols->reserved2)))
+ return -EFAULT;
+ /* Note: if the void * part of the union ever becomes relevant
+ then we need to know the type of the control in order to do
+ the right thing here. Luckily, that is not yet an issue. */
+ if (copy_in_user(&ucontrols->value, &kcontrols->value, sizeof(ucontrols->value)))
+ return -EFAULT;
+ ucontrols++;
+ kcontrols++;
}
-
- return native_ioctl(file, VIDIOCSWIN, (unsigned long)vw);
+ return 0;
}
+
+#define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32)
+#define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32)
+#define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32)
+#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32)
+#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32)
+#define VIDIOC_QBUF32 _IOWR('V', 15, struct v4l2_buffer32)
+#define VIDIOC_DQBUF32 _IOWR('V', 17, struct v4l2_buffer32)
+#define VIDIOC_ENUMSTD32 _IOWR('V', 25, struct v4l2_standard32)
+#define VIDIOC_ENUMINPUT32 _IOWR('V', 26, struct v4l2_input32)
+#define VIDIOC_TRY_FMT32 _IOWR('V', 64, struct v4l2_format32)
+#define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32)
+#define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32)
+#define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32)
+
+#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32)
+#ifdef __OLD_VIDIOC_
+#define VIDIOC_OVERLAY32_OLD _IOWR('V', 14, s32)
#endif
+#define VIDIOC_STREAMON32 _IOW ('V', 18, s32)
+#define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32)
+#define VIDIOC_G_INPUT32 _IOR ('V', 38, s32)
+#define VIDIOC_S_INPUT32 _IOWR('V', 39, s32)
+#define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32)
+#define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32)
static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
@@ -624,53 +718,60 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg
struct v4l2_format v2f;
struct v4l2_buffer v2b;
struct v4l2_framebuffer v2fb;
- struct v4l2_standard v2s;
struct v4l2_input v2i;
- struct v4l2_tuner v2t;
+ struct v4l2_standard v2s;
+ struct v4l2_ext_controls v2ecs;
unsigned long vx;
+ int vi;
} karg;
void __user *up = compat_ptr(arg);
int compatible_arg = 1;
int err = 0;
- int realcmd = cmd;
/* First, convert the command. */
- switch(cmd) {
+ switch (cmd) {
#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCGTUNER32: realcmd = cmd = VIDIOCGTUNER; break;
- case VIDIOCSTUNER32: realcmd = cmd = VIDIOCSTUNER; break;
- case VIDIOCGWIN32: realcmd = cmd = VIDIOCGWIN; break;
- case VIDIOCGFBUF32: realcmd = cmd = VIDIOCGFBUF; break;
- case VIDIOCSFBUF32: realcmd = cmd = VIDIOCSFBUF; break;
- case VIDIOCGFREQ32: realcmd = cmd = VIDIOCGFREQ; break;
- case VIDIOCSFREQ32: realcmd = cmd = VIDIOCSFREQ; break;
- case VIDIOCSMICROCODE32: realcmd = cmd = VIDIOCSMICROCODE; break;
+ case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break;
+ case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break;
+ case VIDIOCGWIN32: cmd = VIDIOCGWIN; break;
+ case VIDIOCSWIN32: cmd = VIDIOCSWIN; break;
+ case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break;
+ case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break;
+ case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break;
+ case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break;
+ case VIDIOCSMICROCODE32: cmd = VIDIOCSMICROCODE; break;
#endif
- case VIDIOC_G_FMT32: realcmd = cmd = VIDIOC_G_FMT; break;
- case VIDIOC_S_FMT32: realcmd = cmd = VIDIOC_S_FMT; break;
- case VIDIOC_QUERYBUF32: realcmd = cmd = VIDIOC_QUERYBUF; break;
- case VIDIOC_QBUF32: realcmd = cmd = VIDIOC_QBUF; break;
- case VIDIOC_DQBUF32: realcmd = cmd = VIDIOC_DQBUF; break;
- case VIDIOC_STREAMON32: realcmd = cmd = VIDIOC_STREAMON; break;
- case VIDIOC_STREAMOFF32: realcmd = cmd = VIDIOC_STREAMOFF; break;
- case VIDIOC_G_FBUF32: realcmd = cmd = VIDIOC_G_FBUF; break;
- case VIDIOC_S_FBUF32: realcmd = cmd = VIDIOC_S_FBUF; break;
- case VIDIOC_OVERLAY32: realcmd = cmd = VIDIOC_OVERLAY; break;
- case VIDIOC_ENUMSTD32: realcmd = VIDIOC_ENUMSTD; break;
- case VIDIOC_ENUMINPUT32: realcmd = VIDIOC_ENUMINPUT; break;
- case VIDIOC_S_CTRL32: realcmd = cmd = VIDIOC_S_CTRL; break;
- case VIDIOC_G_INPUT32: realcmd = cmd = VIDIOC_G_INPUT; break;
- case VIDIOC_S_INPUT32: realcmd = cmd = VIDIOC_S_INPUT; break;
- case VIDIOC_TRY_FMT32: realcmd = cmd = VIDIOC_TRY_FMT; break;
- };
-
- switch(cmd) {
+ case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break;
+ case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break;
+ case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break;
+ case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break;
+ case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break;
+ case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break;
+ case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break;
+ case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break;
+ case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break;
+ case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break;
+ case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break;
+ case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break;
+ case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break;
+ case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break;
+#ifdef __OLD_VIDIOC_
+ case VIDIOC_OVERLAY32_OLD: cmd = VIDIOC_OVERLAY; break;
+#endif
+ case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break;
+ case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break;
+ case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break;
+ case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break;
+ case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break;
+ case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break;
+ }
+
+ switch (cmd) {
#ifdef CONFIG_VIDEO_V4L1_COMPAT
case VIDIOCSTUNER:
case VIDIOCGTUNER:
err = get_video_tuner32(&karg.vt, up);
compatible_arg = 0;
-
break;
case VIDIOCSFBUF:
@@ -678,19 +779,42 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg
compatible_arg = 0;
break;
+ case VIDIOCSWIN:
+ err = get_video_window32(&karg.vw, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOCGWIN:
+ case VIDIOCGFBUF:
+ case VIDIOCGFREQ:
+ compatible_arg = 0;
+ break;
+
+ case VIDIOCSMICROCODE:
+ err = get_microcode32(&karg.vc, up);
+ compatible_arg = 0;
+ break;
case VIDIOCSFREQ:
+ err = get_user(karg.vx, (u32 __user *)up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOCCAPTURE:
+ case VIDIOCSYNC:
+ case VIDIOCSWRITEMODE:
#endif
- case VIDIOC_S_INPUT:
case VIDIOC_OVERLAY:
case VIDIOC_STREAMON:
case VIDIOC_STREAMOFF:
- err = get_user(karg.vx, (u32 __user *)up);
- compatible_arg = 1;
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_OUTPUT:
+ err = get_user(karg.vi, (s32 __user *)up);
+ compatible_arg = 0;
break;
- case VIDIOC_S_FBUF:
- err = get_v4l2_framebuffer32(&karg.v2fb, up);
+ case VIDIOC_G_INPUT:
+ case VIDIOC_G_OUTPUT:
compatible_arg = 0;
break;
@@ -708,122 +832,108 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg
compatible_arg = 0;
break;
- case VIDIOC_ENUMSTD:
- err = get_v4l2_standard(&karg.v2s, up);
+ case VIDIOC_S_FBUF:
+ err = get_v4l2_framebuffer32(&karg.v2fb, up);
compatible_arg = 0;
break;
- case VIDIOC_ENUMSTD32:
- err = get_v4l2_standard32(&karg.v2s, up);
+ case VIDIOC_G_FBUF:
compatible_arg = 0;
break;
- case VIDIOC_ENUMINPUT:
- err = get_v4l2_input(&karg.v2i, up);
+ case VIDIOC_ENUMSTD:
+ err = get_v4l2_standard32(&karg.v2s, up);
compatible_arg = 0;
break;
- case VIDIOC_ENUMINPUT32:
+ case VIDIOC_ENUMINPUT:
err = get_v4l2_input32(&karg.v2i, up);
compatible_arg = 0;
break;
- case VIDIOC_G_TUNER:
- case VIDIOC_S_TUNER:
- err = get_v4l2_tuner(&karg.v2t, up);
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ err = get_v4l2_ext_controls32(&karg.v2ecs, up);
compatible_arg = 0;
break;
+ }
+ if (err)
+ return err;
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCGWIN:
- case VIDIOCGFBUF:
- case VIDIOCGFREQ:
-#endif
- case VIDIOC_G_FBUF:
- case VIDIOC_G_INPUT:
- compatible_arg = 0;
- break;
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCSMICROCODE:
- err = microcode32(&karg.vc, up);
- compatible_arg = 0;
- break;
-#endif
- };
- if(err)
- goto out;
-
- if(compatible_arg)
- err = native_ioctl(file, realcmd, (unsigned long)up);
+ if (compatible_arg)
+ err = native_ioctl(file, cmd, (unsigned long)up);
else {
mm_segment_t old_fs = get_fs();
set_fs(KERNEL_DS);
- err = native_ioctl(file, realcmd, (unsigned long) &karg);
+ err = native_ioctl(file, cmd, (unsigned long)&karg);
set_fs(old_fs);
}
- if(err == 0) {
- switch(cmd) {
+
+ /* Special case: even after an error we need to put the
+ results back for these ioctls since the error_idx will
+ contain information on which control failed. */
+ switch (cmd) {
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ if (put_v4l2_ext_controls32(&karg.v2ecs, up))
+ err = -EFAULT;
+ break;
+ }
+ if (err)
+ return err;
+
+ switch (cmd) {
#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCGTUNER:
- err = put_video_tuner32(&karg.vt, up);
- break;
+ case VIDIOCGTUNER:
+ err = put_video_tuner32(&karg.vt, up);
+ break;
- case VIDIOCGWIN:
- err = put_video_window32(&karg.vw, up);
- break;
+ case VIDIOCGWIN:
+ err = put_video_window32(&karg.vw, up);
+ break;
- case VIDIOCGFBUF:
- err = put_video_buffer32(&karg.vb, up);
- break;
+ case VIDIOCGFBUF:
+ err = put_video_buffer32(&karg.vb, up);
+ break;
+ case VIDIOCGFREQ:
+ err = put_user(((u32)karg.vx), (u32 __user *)up);
+ break;
#endif
- case VIDIOC_G_FBUF:
- err = put_v4l2_framebuffer32(&karg.v2fb, up);
- break;
-
- case VIDIOC_G_FMT:
- case VIDIOC_S_FMT:
- case VIDIOC_TRY_FMT:
- err = put_v4l2_format32(&karg.v2f, up);
- break;
-
- case VIDIOC_QUERYBUF:
- case VIDIOC_QBUF:
- case VIDIOC_DQBUF:
- err = put_v4l2_buffer32(&karg.v2b, up);
- break;
-
- case VIDIOC_ENUMSTD:
- err = put_v4l2_standard(&karg.v2s, up);
- break;
-
- case VIDIOC_ENUMSTD32:
- err = put_v4l2_standard32(&karg.v2s, up);
- break;
-
- case VIDIOC_G_TUNER:
- case VIDIOC_S_TUNER:
- err = put_v4l2_tuner(&karg.v2t, up);
- break;
-
- case VIDIOC_ENUMINPUT:
- err = put_v4l2_input(&karg.v2i, up);
- break;
-
- case VIDIOC_ENUMINPUT32:
- err = put_v4l2_input32(&karg.v2i, up);
- break;
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_OUTPUT:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_G_OUTPUT:
+ err = put_user(((s32)karg.vi), (s32 __user *)up);
+ break;
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCGFREQ:
-#endif
- case VIDIOC_G_INPUT:
- err = put_user(((u32)karg.vx), (u32 __user *)up);
- break;
- };
+ case VIDIOC_G_FBUF:
+ err = put_v4l2_framebuffer32(&karg.v2fb, up);
+ break;
+
+ case VIDIOC_G_FMT:
+ case VIDIOC_S_FMT:
+ case VIDIOC_TRY_FMT:
+ err = put_v4l2_format32(&karg.v2f, up);
+ break;
+
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_QBUF:
+ case VIDIOC_DQBUF:
+ err = put_v4l2_buffer32(&karg.v2b, up);
+ break;
+
+ case VIDIOC_ENUMSTD:
+ err = put_v4l2_standard32(&karg.v2s, up);
+ break;
+
+ case VIDIOC_ENUMINPUT:
+ err = put_v4l2_input32(&karg.v2i, up);
+ break;
}
-out:
return err;
}
@@ -836,26 +946,48 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
switch (cmd) {
#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCSWIN32:
- ret = do_set_window(file, cmd, arg);
- break;
+ case VIDIOCGCAP:
+ case VIDIOCGCHAN:
+ case VIDIOCSCHAN:
case VIDIOCGTUNER32:
case VIDIOCSTUNER32:
+ case VIDIOCGPICT:
+ case VIDIOCSPICT:
+ case VIDIOCCAPTURE32:
case VIDIOCGWIN32:
+ case VIDIOCSWIN32:
case VIDIOCGFBUF32:
case VIDIOCSFBUF32:
+ case VIDIOCKEY:
case VIDIOCGFREQ32:
case VIDIOCSFREQ32:
case VIDIOCGAUDIO:
case VIDIOCSAUDIO:
+ case VIDIOCSYNC32:
+ case VIDIOCMCAPTURE:
+ case VIDIOCGMBUF:
+ case VIDIOCGUNIT:
+ case VIDIOCGCAPTURE:
+ case VIDIOCSCAPTURE:
+ case VIDIOCSPLAYMODE:
+ case VIDIOCSWRITEMODE32:
+ case VIDIOCGPLAYINFO:
+ case VIDIOCSMICROCODE32:
case VIDIOCGVBIFMT:
case VIDIOCSVBIFMT:
#endif
+#ifdef __OLD_VIDIOC_
+ case VIDIOC_OVERLAY32_OLD:
+ case VIDIOC_S_PARM_OLD:
+ case VIDIOC_S_CTRL_OLD:
+ case VIDIOC_G_AUDIO_OLD:
+ case VIDIOC_G_AUDOUT_OLD:
+ case VIDIOC_CROPCAP_OLD:
+#endif
case VIDIOC_QUERYCAP:
+ case VIDIOC_RESERVED:
case VIDIOC_ENUM_FMT:
case VIDIOC_G_FMT32:
- case VIDIOC_CROPCAP:
- case VIDIOC_S_CROP:
case VIDIOC_S_FMT32:
case VIDIOC_REQBUFS:
case VIDIOC_QUERYBUF32:
@@ -870,43 +1002,56 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
case VIDIOC_S_PARM:
case VIDIOC_G_STD:
case VIDIOC_S_STD:
- case VIDIOC_G_TUNER:
- case VIDIOC_S_TUNER:
- case VIDIOC_ENUMSTD:
case VIDIOC_ENUMSTD32:
- case VIDIOC_ENUMINPUT:
case VIDIOC_ENUMINPUT32:
case VIDIOC_G_CTRL:
case VIDIOC_S_CTRL:
- case VIDIOC_S_CTRL32:
- case VIDIOC_S_FREQUENCY:
- case VIDIOC_G_FREQUENCY:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_AUDIO:
+ case VIDIOC_S_AUDIO:
case VIDIOC_QUERYCTRL:
+ case VIDIOC_QUERYMENU:
case VIDIOC_G_INPUT32:
case VIDIOC_S_INPUT32:
+ case VIDIOC_G_OUTPUT32:
+ case VIDIOC_S_OUTPUT32:
+ case VIDIOC_ENUMOUTPUT:
+ case VIDIOC_G_AUDOUT:
+ case VIDIOC_S_AUDOUT:
+ case VIDIOC_G_MODULATOR:
+ case VIDIOC_S_MODULATOR:
+ case VIDIOC_S_FREQUENCY:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_CROPCAP:
+ case VIDIOC_G_CROP:
+ case VIDIOC_S_CROP:
+ case VIDIOC_G_JPEGCOMP:
+ case VIDIOC_S_JPEGCOMP:
+ case VIDIOC_QUERYSTD:
case VIDIOC_TRY_FMT32:
- case VIDIOC_S_HW_FREQ_SEEK:
+ case VIDIOC_ENUMAUDIO:
+ case VIDIOC_ENUMAUDOUT:
+ case VIDIOC_G_PRIORITY:
+ case VIDIOC_S_PRIORITY:
+ case VIDIOC_G_SLICED_VBI_CAP:
+ case VIDIOC_LOG_STATUS:
+ case VIDIOC_G_EXT_CTRLS32:
+ case VIDIOC_S_EXT_CTRLS32:
+ case VIDIOC_TRY_EXT_CTRLS32:
case VIDIOC_ENUM_FRAMESIZES:
case VIDIOC_ENUM_FRAMEINTERVALS:
+ case VIDIOC_G_ENC_INDEX:
+ case VIDIOC_ENCODER_CMD:
+ case VIDIOC_TRY_ENCODER_CMD:
+ case VIDIOC_DBG_S_REGISTER:
+ case VIDIOC_DBG_G_REGISTER:
+ case VIDIOC_G_CHIP_IDENT:
+ case VIDIOC_S_HW_FREQ_SEEK:
ret = do_video_ioctl(file, cmd, arg);
break;
#ifdef CONFIG_VIDEO_V4L1_COMPAT
- /* Little v, the video4linux ioctls (conflict?) */
- case VIDIOCGCAP:
- case VIDIOCGCHAN:
- case VIDIOCSCHAN:
- case VIDIOCGPICT:
- case VIDIOCSPICT:
- case VIDIOCCAPTURE:
- case VIDIOCKEY:
- case VIDIOCSYNC:
- case VIDIOCMCAPTURE:
- case VIDIOCGMBUF:
- case VIDIOCGUNIT:
- case VIDIOCGCAPTURE:
- case VIDIOCSCAPTURE:
-
/* BTTV specific... */
case _IOW('v', BASE_VIDIOCPRIVATE+0, char [256]):
case _IOR('v', BASE_VIDIOCPRIVATE+1, char [256]):
@@ -921,6 +1066,8 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
#endif
default:
v4l_print_ioctl("compat_ioctl32", cmd);
+ printk(KERN_CONT "\n");
+ break;
}
return ret;
}
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index ccd6566a515e..7ad6711ee327 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -9,7 +9,7 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Authors: Alan Cox, <alan@redhat.com> (version 1)
+ * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
* Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
*
* Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com>
@@ -30,6 +30,7 @@
#include <asm/system.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#define VIDEO_NUM_DEVICES 256
#define VIDEO_NAME "video4linux"
@@ -41,17 +42,17 @@
static ssize_t show_index(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
- return sprintf(buf, "%i\n", vfd->index);
+ return sprintf(buf, "%i\n", vdev->index);
}
static ssize_t show_name(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
- return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name);
+ return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name);
}
static struct device_attribute video_device_attrs[] = {
@@ -73,64 +74,64 @@ struct video_device *video_device_alloc(void)
}
EXPORT_SYMBOL(video_device_alloc);
-void video_device_release(struct video_device *vfd)
+void video_device_release(struct video_device *vdev)
{
- kfree(vfd);
+ kfree(vdev);
}
EXPORT_SYMBOL(video_device_release);
-void video_device_release_empty(struct video_device *vfd)
+void video_device_release_empty(struct video_device *vdev)
{
/* Do nothing */
/* Only valid when the video_device struct is a static. */
}
EXPORT_SYMBOL(video_device_release_empty);
-/* Called when the last user of the character device is gone. */
-static void v4l2_chardev_release(struct kobject *kobj)
+static inline void video_get(struct video_device *vdev)
{
- struct video_device *vfd = container_of(kobj, struct video_device, cdev.kobj);
+ get_device(&vdev->dev);
+}
+
+static inline void video_put(struct video_device *vdev)
+{
+ put_device(&vdev->dev);
+}
+
+/* Called when the last user of the video device exits. */
+static void v4l2_device_release(struct device *cd)
+{
+ struct video_device *vdev = to_video_device(cd);
mutex_lock(&videodev_lock);
- if (video_device[vfd->minor] != vfd) {
+ if (video_device[vdev->minor] != vdev) {
mutex_unlock(&videodev_lock);
- BUG();
+ /* should not happen */
+ WARN_ON(1);
return;
}
/* Free up this device for reuse */
- video_device[vfd->minor] = NULL;
- clear_bit(vfd->num, video_nums[vfd->vfl_type]);
- mutex_unlock(&videodev_lock);
+ video_device[vdev->minor] = NULL;
- /* Release the character device */
- vfd->cdev_release(kobj);
- /* Release video_device and perform other
- cleanups as needed. */
- if (vfd->release)
- vfd->release(vfd);
-}
+ /* Delete the cdev on this minor as well */
+ cdev_del(vdev->cdev);
+ /* Just in case some driver tries to access this from
+ the release() callback. */
+ vdev->cdev = NULL;
-/* The new kobj_type for the character device */
-static struct kobj_type v4l2_ktype_cdev_default = {
- .release = v4l2_chardev_release,
-};
+ /* Mark minor as free */
+ clear_bit(vdev->num, video_nums[vdev->vfl_type]);
-static void video_release(struct device *cd)
-{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
+ mutex_unlock(&videodev_lock);
- /* It's now safe to delete the char device.
- This will either trigger the v4l2_chardev_release immediately (if
- the refcount goes to 0) or later when the last user of the
- character device closes it. */
- cdev_del(&vfd->cdev);
+ /* Release video_device and perform other
+ cleanups as needed. */
+ vdev->release(vdev);
}
static struct class video_class = {
.name = VIDEO_NAME,
.dev_attrs = video_device_attrs,
- .dev_release = video_release,
};
struct video_device *video_devdata(struct file *file)
@@ -139,13 +140,163 @@ struct video_device *video_devdata(struct file *file)
}
EXPORT_SYMBOL(video_devdata);
+static ssize_t v4l2_read(struct file *filp, char __user *buf,
+ size_t sz, loff_t *off)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->read)
+ return -EINVAL;
+ if (video_is_unregistered(vdev))
+ return -EIO;
+ return vdev->fops->read(filp, buf, sz, off);
+}
+
+static ssize_t v4l2_write(struct file *filp, const char __user *buf,
+ size_t sz, loff_t *off)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->write)
+ return -EINVAL;
+ if (video_is_unregistered(vdev))
+ return -EIO;
+ return vdev->fops->write(filp, buf, sz, off);
+}
+
+static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->poll || video_is_unregistered(vdev))
+ return DEFAULT_POLLMASK;
+ return vdev->fops->poll(filp, poll);
+}
+
+static int v4l2_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->ioctl)
+ return -ENOTTY;
+ /* Allow ioctl to continue even if the device was unregistered.
+ Things like dequeueing buffers might still be useful. */
+ return vdev->fops->ioctl(inode, filp, cmd, arg);
+}
+
+static long v4l2_unlocked_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->unlocked_ioctl)
+ return -ENOTTY;
+ /* Allow ioctl to continue even if the device was unregistered.
+ Things like dequeueing buffers might still be useful. */
+ return vdev->fops->unlocked_ioctl(filp, cmd, arg);
+}
+
+#ifdef CONFIG_COMPAT
+static long v4l2_compat_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->compat_ioctl)
+ return -ENOIOCTLCMD;
+ /* Allow ioctl to continue even if the device was unregistered.
+ Things like dequeueing buffers might still be useful. */
+ return vdev->fops->compat_ioctl(filp, cmd, arg);
+}
+#endif
+
+static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->mmap ||
+ video_is_unregistered(vdev))
+ return -ENODEV;
+ return vdev->fops->mmap(filp, vm);
+}
+
+/* Override for the open function */
+static int v4l2_open(struct inode *inode, struct file *filp)
+{
+ struct video_device *vdev;
+ int ret;
+
+ /* Check if the video device is available */
+ mutex_lock(&videodev_lock);
+ vdev = video_devdata(filp);
+ /* return ENODEV if the video device has been removed
+ already or if it is not registered anymore. */
+ if (vdev == NULL || video_is_unregistered(vdev)) {
+ mutex_unlock(&videodev_lock);
+ return -ENODEV;
+ }
+ /* and increase the device refcount */
+ video_get(vdev);
+ mutex_unlock(&videodev_lock);
+ ret = vdev->fops->open(inode, filp);
+ /* decrease the refcount in case of an error */
+ if (ret)
+ video_put(vdev);
+ return ret;
+}
+
+/* Override for the release function */
+static int v4l2_release(struct inode *inode, struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = vdev->fops->release(inode, filp);
+
+ /* decrease the refcount unconditionally since the release()
+ return value is ignored. */
+ video_put(vdev);
+ return ret;
+}
+
+static const struct file_operations v4l2_unlocked_fops = {
+ .owner = THIS_MODULE,
+ .read = v4l2_read,
+ .write = v4l2_write,
+ .open = v4l2_open,
+ .mmap = v4l2_mmap,
+ .unlocked_ioctl = v4l2_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = v4l2_compat_ioctl,
+#endif
+ .release = v4l2_release,
+ .poll = v4l2_poll,
+ .llseek = no_llseek,
+};
+
+static const struct file_operations v4l2_fops = {
+ .owner = THIS_MODULE,
+ .read = v4l2_read,
+ .write = v4l2_write,
+ .open = v4l2_open,
+ .mmap = v4l2_mmap,
+ .ioctl = v4l2_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = v4l2_compat_ioctl,
+#endif
+ .release = v4l2_release,
+ .poll = v4l2_poll,
+ .llseek = no_llseek,
+};
+
/**
* get_index - assign stream number based on parent device
- * @vdev: video_device to assign index number to, vdev->dev should be assigned
- * @num: -1 if auto assign, requested number otherwise
+ * @vdev: video_device to assign index number to, vdev->parent should be assigned
+ * @num: -1 if auto assign, requested number otherwise
*
+ * Note that when this is called the new device has not yet been registered
+ * in the video_device array.
*
- * returns -ENFILE if num is already in use, a free index number if
+ * Returns -ENFILE if num is already in use, a free index number if
* successful.
*/
static int get_index(struct video_device *vdev, int num)
@@ -162,9 +313,12 @@ static int get_index(struct video_device *vdev, int num)
return -EINVAL;
}
+ /* Some drivers do not set the parent. In that case always return 0. */
+ if (vdev->parent == NULL)
+ return 0;
+
for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
if (video_device[i] != NULL &&
- video_device[i] != vdev &&
video_device[i]->parent == vdev->parent) {
used |= 1 << video_device[i]->index;
}
@@ -180,17 +334,15 @@ static int get_index(struct video_device *vdev, int num)
return i > max_index ? -ENFILE : i;
}
-static const struct file_operations video_fops;
-
-int video_register_device(struct video_device *vfd, int type, int nr)
+int video_register_device(struct video_device *vdev, int type, int nr)
{
- return video_register_device_index(vfd, type, nr, -1);
+ return video_register_device_index(vdev, type, nr, -1);
}
EXPORT_SYMBOL(video_register_device);
/**
* video_register_device_index - register video4linux devices
- * @vfd: video device structure we want to register
+ * @vdev: video device structure we want to register
* @type: type of device to register
* @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ...
* -1 == first free)
@@ -214,8 +366,7 @@ EXPORT_SYMBOL(video_register_device);
*
* %VFL_TYPE_RADIO - A radio card
*/
-
-int video_register_device_index(struct video_device *vfd, int type, int nr,
+int video_register_device_index(struct video_device *vdev, int type, int nr,
int index)
{
int i = 0;
@@ -223,14 +374,19 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
- void *priv = video_get_drvdata(vfd);
+ void *priv = video_get_drvdata(vdev);
- /* the release callback MUST be present */
- BUG_ON(!vfd->release);
+ /* A minor value of -1 marks this video device as never
+ having been registered */
+ if (vdev)
+ vdev->minor = -1;
- if (vfd == NULL)
+ /* the release callback MUST be present */
+ WARN_ON(!vdev || !vdev->release);
+ if (!vdev || !vdev->release)
return -EINVAL;
+ /* Part 1: check device type */
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
@@ -250,8 +406,12 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
return -EINVAL;
}
- vfd->vfl_type = type;
+ vdev->vfl_type = type;
+ vdev->cdev = NULL;
+ if (vdev->v4l2_dev)
+ vdev->parent = vdev->v4l2_dev->dev;
+ /* Part 2: find a free minor, kernel number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first four types for historical
* reasons.
@@ -282,10 +442,7 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
}
#endif
- /* Initialize the character device */
- cdev_init(&vfd->cdev, vfd->fops);
- vfd->cdev.owner = vfd->fops->owner;
- /* pick a minor number */
+ /* Pick a minor number */
mutex_lock(&videodev_lock);
nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr);
if (nr == minor_cnt)
@@ -309,72 +466,92 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
return -ENFILE;
}
#endif
- vfd->minor = i + minor_offset;
- vfd->num = nr;
+ vdev->minor = i + minor_offset;
+ vdev->num = nr;
set_bit(nr, video_nums[type]);
- BUG_ON(video_device[vfd->minor]);
- video_device[vfd->minor] = vfd;
-
- ret = get_index(vfd, index);
- vfd->index = ret;
-
+ /* Should not happen since we thought this minor was free */
+ WARN_ON(video_device[vdev->minor] != NULL);
+ ret = vdev->index = get_index(vdev, index);
mutex_unlock(&videodev_lock);
if (ret < 0) {
printk(KERN_ERR "%s: get_index failed\n", __func__);
- goto fail_minor;
+ goto cleanup;
}
- ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1);
+ /* Part 3: Initialize the character device */
+ vdev->cdev = cdev_alloc();
+ if (vdev->cdev == NULL) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ if (vdev->fops->unlocked_ioctl)
+ vdev->cdev->ops = &v4l2_unlocked_fops;
+ else
+ vdev->cdev->ops = &v4l2_fops;
+ vdev->cdev->owner = vdev->fops->owner;
+ ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
- goto fail_minor;
+ kfree(vdev->cdev);
+ vdev->cdev = NULL;
+ goto cleanup;
}
- /* sysfs class */
- memset(&vfd->dev, 0, sizeof(vfd->dev));
+
+ /* Part 4: register the device with sysfs */
+ memset(&vdev->dev, 0, sizeof(vdev->dev));
/* The memset above cleared the device's drvdata, so
put back the copy we made earlier. */
- video_set_drvdata(vfd, priv);
- vfd->dev.class = &video_class;
- vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor);
- if (vfd->parent)
- vfd->dev.parent = vfd->parent;
- sprintf(vfd->dev.bus_id, "%s%d", name_base, nr);
- ret = device_register(&vfd->dev);
+ video_set_drvdata(vdev, priv);
+ vdev->dev.class = &video_class;
+ vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
+ if (vdev->parent)
+ vdev->dev.parent = vdev->parent;
+ dev_set_name(&vdev->dev, "%s%d", name_base, nr);
+ ret = device_register(&vdev->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
- goto del_cdev;
+ goto cleanup;
}
- /* Remember the cdev's release function */
- vfd->cdev_release = vfd->cdev.kobj.ktype->release;
- /* Install our own */
- vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default;
- return 0;
+ /* Register the release callback that will be called when the last
+ reference to the device goes away. */
+ vdev->dev.release = v4l2_device_release;
-del_cdev:
- cdev_del(&vfd->cdev);
+ /* Part 5: Activate this minor. The char device can now be used. */
+ mutex_lock(&videodev_lock);
+ video_device[vdev->minor] = vdev;
+ mutex_unlock(&videodev_lock);
+ return 0;
-fail_minor:
+cleanup:
mutex_lock(&videodev_lock);
- video_device[vfd->minor] = NULL;
- clear_bit(vfd->num, video_nums[type]);
+ if (vdev->cdev)
+ cdev_del(vdev->cdev);
+ clear_bit(vdev->num, video_nums[type]);
mutex_unlock(&videodev_lock);
- vfd->minor = -1;
+ /* Mark this video device as never having been registered. */
+ vdev->minor = -1;
return ret;
}
EXPORT_SYMBOL(video_register_device_index);
/**
* video_unregister_device - unregister a video4linux device
- * @vfd: the device to unregister
+ * @vdev: the device to unregister
*
- * This unregisters the passed device and deassigns the minor
- * number. Future open calls will be met with errors.
+ * This unregisters the passed device. Future open calls will
+ * be met with errors.
*/
-
-void video_unregister_device(struct video_device *vfd)
+void video_unregister_device(struct video_device *vdev)
{
- device_unregister(&vfd->dev);
+ /* Check if vdev was ever registered at all */
+ if (!vdev || vdev->minor < 0)
+ return;
+
+ mutex_lock(&videodev_lock);
+ set_bit(V4L2_FL_UNREGISTERED, &vdev->flags);
+ mutex_unlock(&videodev_lock);
+ device_unregister(&vdev->dev);
}
EXPORT_SYMBOL(video_unregister_device);
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
new file mode 100644
index 000000000000..9eefde031597
--- /dev/null
+++ b/drivers/media/video/v4l2-device.c
@@ -0,0 +1,86 @@
+/*
+ V4L2 device support.
+
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+
+int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
+{
+ if (dev == NULL || v4l2_dev == NULL)
+ return -EINVAL;
+ /* Warn if we apparently re-register a device */
+ WARN_ON(dev_get_drvdata(dev));
+ INIT_LIST_HEAD(&v4l2_dev->subdevs);
+ spin_lock_init(&v4l2_dev->lock);
+ v4l2_dev->dev = dev;
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
+ dev->driver->name, dev->bus_id);
+ dev_set_drvdata(dev, v4l2_dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register);
+
+void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
+{
+ struct v4l2_subdev *sd, *next;
+
+ if (v4l2_dev == NULL || v4l2_dev->dev == NULL)
+ return;
+ dev_set_drvdata(v4l2_dev->dev, NULL);
+ /* unregister subdevs */
+ list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list)
+ v4l2_device_unregister_subdev(sd);
+
+ v4l2_dev->dev = NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_unregister);
+
+int v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd)
+{
+ /* Check for valid input */
+ if (dev == NULL || sd == NULL || !sd->name[0])
+ return -EINVAL;
+ /* Warn if we apparently re-register a subdev */
+ WARN_ON(sd->dev);
+ if (!try_module_get(sd->owner))
+ return -ENODEV;
+ sd->dev = dev;
+ spin_lock(&dev->lock);
+ list_add_tail(&sd->list, &dev->subdevs);
+ spin_unlock(&dev->lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
+
+void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
+{
+ /* return if it isn't registered */
+ if (sd == NULL || sd->dev == NULL)
+ return;
+ spin_lock(&sd->dev->lock);
+ list_del(&sd->list);
+ spin_unlock(&sd->dev->lock);
+ sd->dev = NULL;
+ module_put(sd->owner);
+}
+EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 710e1a40c422..b063381f4b3b 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -8,7 +8,7 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Authors: Alan Cox, <alan@redhat.com> (version 1)
+ * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
* Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
*/
@@ -393,10 +393,8 @@ video_fix_command(unsigned int cmd)
* Obsolete usercopy function - Should be removed soon
*/
int
-video_usercopy(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg,
- int (*func)(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg))
+video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
+ v4l2_kioctl func)
{
char sbuf[128];
void *mbuf = NULL;
@@ -458,7 +456,7 @@ video_usercopy(struct inode *inode, struct file *file,
}
/* call driver */
- err = func(inode, file, cmd, parg);
+ err = func(file, cmd, parg);
if (err == -ENOIOCTLCMD)
err = -EINVAL;
if (is_ext_ctrl) {
@@ -1481,9 +1479,15 @@ static int __video_do_ioctl(struct file *file,
case VIDIOC_G_CROP:
{
struct v4l2_crop *p = arg;
+ __u32 type;
if (!ops->vidioc_g_crop)
break;
+
+ type = p->type;
+ memset(p, 0, sizeof(*p));
+ p->type = type;
+
dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
ret = ops->vidioc_g_crop(file, fh, p);
if (!ret)
@@ -1504,10 +1508,16 @@ static int __video_do_ioctl(struct file *file,
case VIDIOC_CROPCAP:
{
struct v4l2_cropcap *p = arg;
+ __u32 type;
/*FIXME: Should also show v4l2_fract pixelaspect */
if (!ops->vidioc_cropcap)
break;
+
+ type = p->type;
+ memset(p, 0, sizeof(*p));
+ p->type = type;
+
dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
ret = ops->vidioc_cropcap(file, fh, p);
if (!ret) {
@@ -1522,6 +1532,9 @@ static int __video_do_ioctl(struct file *file,
if (!ops->vidioc_g_jpegcomp)
break;
+
+ memset(p, 0, sizeof(*p));
+
ret = ops->vidioc_g_jpegcomp(file, fh, p);
if (!ret)
dbgarg(cmd, "quality=%d, APPn=%d, "
@@ -1749,6 +1762,77 @@ static int __video_do_ioctl(struct file *file,
ret = ops->vidioc_s_hw_freq_seek(file, fh, p);
break;
}
+ case VIDIOC_ENUM_FRAMESIZES:
+ {
+ struct v4l2_frmsizeenum *p = arg;
+
+ if (!ops->vidioc_enum_framesizes)
+ break;
+
+ memset(p, 0, sizeof(*p));
+
+ ret = ops->vidioc_enum_framesizes(file, fh, p);
+ dbgarg(cmd,
+ "index=%d, pixelformat=%d, type=%d ",
+ p->index, p->pixel_format, p->type);
+ switch (p->type) {
+ case V4L2_FRMSIZE_TYPE_DISCRETE:
+ dbgarg2("width = %d, height=%d\n",
+ p->discrete.width, p->discrete.height);
+ break;
+ case V4L2_FRMSIZE_TYPE_STEPWISE:
+ dbgarg2("min %dx%d, max %dx%d, step %dx%d\n",
+ p->stepwise.min_width, p->stepwise.min_height,
+ p->stepwise.step_width, p->stepwise.step_height,
+ p->stepwise.max_width, p->stepwise.max_height);
+ break;
+ case V4L2_FRMSIZE_TYPE_CONTINUOUS:
+ dbgarg2("continuous\n");
+ break;
+ default:
+ dbgarg2("- Unknown type!\n");
+ }
+
+ break;
+ }
+ case VIDIOC_ENUM_FRAMEINTERVALS:
+ {
+ struct v4l2_frmivalenum *p = arg;
+
+ if (!ops->vidioc_enum_frameintervals)
+ break;
+
+ memset(p, 0, sizeof(*p));
+
+ ret = ops->vidioc_enum_frameintervals(file, fh, p);
+ dbgarg(cmd,
+ "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ",
+ p->index, p->pixel_format,
+ p->width, p->height, p->type);
+ switch (p->type) {
+ case V4L2_FRMIVAL_TYPE_DISCRETE:
+ dbgarg2("fps=%d/%d\n",
+ p->discrete.numerator,
+ p->discrete.denominator);
+ break;
+ case V4L2_FRMIVAL_TYPE_STEPWISE:
+ dbgarg2("min=%d/%d, max=%d/%d, step=%d/%d\n",
+ p->stepwise.min.numerator,
+ p->stepwise.min.denominator,
+ p->stepwise.max.numerator,
+ p->stepwise.max.denominator,
+ p->stepwise.step.numerator,
+ p->stepwise.step.denominator);
+ break;
+ case V4L2_FRMIVAL_TYPE_CONTINUOUS:
+ dbgarg2("continuous\n");
+ break;
+ default:
+ dbgarg2("- Unknown type!\n");
+ }
+ break;
+ }
+
default:
{
if (!ops->vidioc_default)
@@ -1768,7 +1852,7 @@ static int __video_do_ioctl(struct file *file,
return ret;
}
-int __video_ioctl2(struct file *file,
+long __video_ioctl2(struct file *file,
unsigned int cmd, unsigned long arg)
{
char sbuf[128];
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
new file mode 100644
index 000000000000..e3612f29d0df
--- /dev/null
+++ b/drivers/media/video/v4l2-subdev.c
@@ -0,0 +1,110 @@
+/*
+ V4L2 sub-device support.
+
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+
+int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg)
+{
+ switch (cmd) {
+ case VIDIOC_QUERYCTRL:
+ return v4l2_subdev_call(sd, core, querymenu, arg);
+ case VIDIOC_G_CTRL:
+ return v4l2_subdev_call(sd, core, g_ctrl, arg);
+ case VIDIOC_S_CTRL:
+ return v4l2_subdev_call(sd, core, s_ctrl, arg);
+ case VIDIOC_QUERYMENU:
+ return v4l2_subdev_call(sd, core, queryctrl, arg);
+ case VIDIOC_LOG_STATUS:
+ return v4l2_subdev_call(sd, core, log_status);
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_subdev_call(sd, core, g_chip_ident, arg);
+ case VIDIOC_INT_S_STANDBY:
+ return v4l2_subdev_call(sd, core, s_standby, arg ? (*(u32 *)arg) : 0);
+ case VIDIOC_INT_RESET:
+ return v4l2_subdev_call(sd, core, reset, arg ? (*(u32 *)arg) : 0);
+ case VIDIOC_INT_S_GPIO:
+ return v4l2_subdev_call(sd, core, s_gpio, arg ? (*(u32 *)arg) : 0);
+ case VIDIOC_INT_INIT:
+ return v4l2_subdev_call(sd, core, init, arg ? (*(u32 *)arg) : 0);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_G_REGISTER:
+ return v4l2_subdev_call(sd, core, g_register, arg);
+ case VIDIOC_DBG_S_REGISTER:
+ return v4l2_subdev_call(sd, core, s_register, arg);
+#endif
+
+ case VIDIOC_INT_S_TUNER_MODE:
+ return v4l2_subdev_call(sd, tuner, s_mode, *(enum v4l2_tuner_type *)arg);
+ case AUDC_SET_RADIO:
+ return v4l2_subdev_call(sd, tuner, s_radio);
+ case VIDIOC_S_TUNER:
+ return v4l2_subdev_call(sd, tuner, s_tuner, arg);
+ case VIDIOC_G_TUNER:
+ return v4l2_subdev_call(sd, tuner, g_tuner, arg);
+ case VIDIOC_S_STD:
+ return v4l2_subdev_call(sd, tuner, s_std, *(v4l2_std_id *)arg);
+ case VIDIOC_S_FREQUENCY:
+ return v4l2_subdev_call(sd, tuner, s_frequency, arg);
+ case VIDIOC_G_FREQUENCY:
+ return v4l2_subdev_call(sd, tuner, g_frequency, arg);
+ case TUNER_SET_TYPE_ADDR:
+ return v4l2_subdev_call(sd, tuner, s_type_addr, arg);
+ case TUNER_SET_CONFIG:
+ return v4l2_subdev_call(sd, tuner, s_config, arg);
+
+ case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+ return v4l2_subdev_call(sd, audio, s_clock_freq, *(u32 *)arg);
+ case VIDIOC_INT_S_AUDIO_ROUTING:
+ return v4l2_subdev_call(sd, audio, s_routing, arg);
+ case VIDIOC_INT_I2S_CLOCK_FREQ:
+ return v4l2_subdev_call(sd, audio, s_i2s_clock_freq, *(u32 *)arg);
+
+ case VIDIOC_INT_S_VIDEO_ROUTING:
+ return v4l2_subdev_call(sd, video, s_routing, arg);
+ case VIDIOC_INT_S_CRYSTAL_FREQ:
+ return v4l2_subdev_call(sd, video, s_crystal_freq, arg);
+ case VIDIOC_INT_DECODE_VBI_LINE:
+ return v4l2_subdev_call(sd, video, decode_vbi_line, arg);
+ case VIDIOC_INT_S_VBI_DATA:
+ return v4l2_subdev_call(sd, video, s_vbi_data, arg);
+ case VIDIOC_INT_G_VBI_DATA:
+ return v4l2_subdev_call(sd, video, g_vbi_data, arg);
+ case VIDIOC_G_SLICED_VBI_CAP:
+ return v4l2_subdev_call(sd, video, g_sliced_vbi_cap, arg);
+ case VIDIOC_S_FMT:
+ return v4l2_subdev_call(sd, video, s_fmt, arg);
+ case VIDIOC_G_FMT:
+ return v4l2_subdev_call(sd, video, g_fmt, arg);
+ case VIDIOC_INT_S_STD_OUTPUT:
+ return v4l2_subdev_call(sd, video, s_std_output, *(v4l2_std_id *)arg);
+ case VIDIOC_STREAMON:
+ return v4l2_subdev_call(sd, video, s_stream, 1);
+ case VIDIOC_STREAMOFF:
+ return v4l2_subdev_call(sd, video, s_stream, 0);
+
+ default:
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+ }
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_command);
diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
index 1efc5f3462c6..a72a361daade 100644
--- a/drivers/media/video/vino.c
+++ b/drivers/media/video/vino.c
@@ -4237,8 +4237,7 @@ error:
return ret;
}
-static int vino_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vino_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct vino_channel_settings *vcs = video_drvdata(file);
@@ -4353,7 +4352,7 @@ static int vino_ioctl(struct inode *inode, struct file *file,
if (mutex_lock_interruptible(&vcs->mutex))
return -EINTR;
- ret = video_usercopy(inode, file, cmd, arg, vino_do_ioctl);
+ ret = video_usercopy(file, cmd, arg, vino_do_ioctl);
mutex_unlock(&vcs->mutex);
diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c
index 577956c5410b..f72b859486ad 100644
--- a/drivers/media/video/vp27smpx.c
+++ b/drivers/media/video/vp27smpx.c
@@ -28,7 +28,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
@@ -40,13 +40,20 @@ MODULE_LICENSE("GPL");
/* ----------------------------------------------------------------------- */
struct vp27smpx_state {
+ struct v4l2_subdev sd;
int radio;
u32 audmode;
};
-static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode)
+static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd)
{
- struct vp27smpx_state *state = i2c_get_clientdata(client);
+ return container_of(sd, struct vp27smpx_state, sd);
+}
+
+static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode)
+{
+ struct vp27smpx_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 data[3] = { 0x00, 0x00, 0x04 };
switch (audmode) {
@@ -63,55 +70,89 @@ static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode)
}
if (i2c_master_send(client, data, sizeof(data)) != sizeof(data))
- v4l_err(client, "%s: I/O error setting audmode\n",
- client->name);
+ v4l2_err(sd, "I/O error setting audmode\n");
else
state->audmode = audmode;
}
-static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int vp27smpx_s_radio(struct v4l2_subdev *sd)
{
- struct vp27smpx_state *state = i2c_get_clientdata(client);
- struct v4l2_tuner *vt = arg;
+ struct vp27smpx_state *state = to_state(sd);
- switch (cmd) {
- case AUDC_SET_RADIO:
- state->radio = 1;
- break;
+ state->radio = 1;
+ return 0;
+}
- case VIDIOC_S_STD:
- state->radio = 0;
- break;
+static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct vp27smpx_state *state = to_state(sd);
- case VIDIOC_S_TUNER:
- if (!state->radio)
- vp27smpx_set_audmode(client, vt->audmode);
- break;
+ state->radio = 0;
+ return 0;
+}
- case VIDIOC_G_TUNER:
- if (state->radio)
- break;
- vt->audmode = state->audmode;
- vt->capability = V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- break;
+static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct vp27smpx_state *state = to_state(sd);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg,
- V4L2_IDENT_VP27SMPX, 0);
+ if (!state->radio)
+ vp27smpx_set_audmode(sd, vt->audmode);
+ return 0;
+}
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Audio Mode: %u%s\n", state->audmode,
- state->radio ? " (Radio)" : "");
- break;
+static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct vp27smpx_state *state = to_state(sd);
+
+ if (state->radio)
+ return 0;
+ vt->audmode = state->audmode;
+ vt->capability = V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ return 0;
+}
- default:
- return -EINVAL;
- }
+static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0);
+}
+
+static int vp27smpx_log_status(struct v4l2_subdev *sd)
+{
+ struct vp27smpx_state *state = to_state(sd);
+
+ v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode,
+ state->radio ? " (Radio)" : "");
return 0;
}
+static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops vp27smpx_core_ops = {
+ .log_status = vp27smpx_log_status,
+ .g_chip_ident = vp27smpx_g_chip_ident,
+};
+
+static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = {
+ .s_radio = vp27smpx_s_radio,
+ .s_std = vp27smpx_s_std,
+ .s_tuner = vp27smpx_s_tuner,
+ .g_tuner = vp27smpx_g_tuner,
+};
+
+static const struct v4l2_subdev_ops vp27smpx_ops = {
+ .core = &vp27smpx_core_ops,
+ .tuner = &vp27smpx_tuner_ops,
+};
+
/* ----------------------------------------------------------------------- */
/* i2c implementation */
@@ -125,6 +166,7 @@ static int vp27smpx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct vp27smpx_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -136,17 +178,21 @@ static int vp27smpx_probe(struct i2c_client *client,
state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops);
state->audmode = V4L2_TUNER_MODE_STEREO;
- i2c_set_clientdata(client, state);
/* initialize vp27smpx */
- vp27smpx_set_audmode(client, state->audmode);
+ vp27smpx_set_audmode(sd, state->audmode);
return 0;
}
static int vp27smpx_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c
index b2dbe48a92bb..56c570c267ea 100644
--- a/drivers/media/video/w9966.c
+++ b/drivers/media/video/w9966.c
@@ -727,8 +727,7 @@ static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data)
* Video4linux interfacing
*/
-static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int w9966_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct w9966_dev *cam = video_drvdata(file);
@@ -881,7 +880,7 @@ static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file,
static int w9966_v4l_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, w9966_v4l_do_ioctl);
+ return video_usercopy(file, cmd, arg, w9966_v4l_do_ioctl);
}
// Capture data
diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c
index 54ac3fe26ec2..12a31e7a5f6d 100644
--- a/drivers/media/video/wm8739.c
+++ b/drivers/media/video/wm8739.c
@@ -28,7 +28,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
@@ -52,6 +52,7 @@ enum {
};
struct wm8739_state {
+ struct v4l2_subdev sd;
u32 clock_freq;
u8 muted;
u16 volume;
@@ -60,43 +61,49 @@ struct wm8739_state {
u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
};
+static inline struct wm8739_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct wm8739_state, sd);
+}
+
/* ------------------------------------------------------------------------ */
-static int wm8739_write(struct i2c_client *client, int reg, u16 val)
+static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
if (reg < 0 || reg >= TOT_REGS) {
- v4l_err(client, "Invalid register R%d\n", reg);
+ v4l2_err(sd, "Invalid register R%d\n", reg);
return -1;
}
- v4l_dbg(1, debug, client, "write: %02x %02x\n", reg, val);
+ v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val);
for (i = 0; i < 3; i++)
if (i2c_smbus_write_byte_data(client,
(reg << 1) | (val >> 8), val & 0xff) == 0)
return 0;
- v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
+ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
return -1;
}
/* write regs to set audio volume etc */
-static void wm8739_set_audio(struct i2c_client *client)
+static void wm8739_set_audio(struct v4l2_subdev *sd)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
+ struct wm8739_state *state = to_state(sd);
u16 mute = state->muted ? 0x80 : 0;
/* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
* Default setting: 0x17 = 0 dB
*/
- wm8739_write(client, R0, (state->vol_l & 0x1f) | mute);
- wm8739_write(client, R1, (state->vol_r & 0x1f) | mute);
+ wm8739_write(sd, R0, (state->vol_l & 0x1f) | mute);
+ wm8739_write(sd, R1, (state->vol_r & 0x1f) | mute);
}
-static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int wm8739_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
+ struct wm8739_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -117,9 +124,9 @@ static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
return 0;
}
-static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
+ struct wm8739_state *state = to_state(sd);
unsigned int work_l, work_r;
switch (ctrl->id) {
@@ -147,7 +154,7 @@ static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
state->vol_r = (long)work_r * 31 / 65535;
/* set audio volume etc. */
- wm8739_set_audio(client);
+ wm8739_set_audio(sd);
return 0;
}
@@ -186,77 +193,89 @@ static struct v4l2_queryctrl wm8739_qctrl[] = {
/* ------------------------------------------------------------------------ */
-static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
-
- switch (cmd) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- {
- u32 audiofreq = *(u32 *)arg;
-
- state->clock_freq = audiofreq;
- /* de-activate */
- wm8739_write(client, R9, 0x000);
- switch (audiofreq) {
- case 44100:
- /* 256fps, fs=44.1k */
- wm8739_write(client, R8, 0x020);
- break;
- case 48000:
- /* 256fps, fs=48k */
- wm8739_write(client, R8, 0x000);
- break;
- case 32000:
- /* 256fps, fs=32k */
- wm8739_write(client, R8, 0x018);
- break;
- default:
- break;
- }
- /* activate */
- wm8739_write(client, R9, 0x001);
+ struct wm8739_state *state = to_state(sd);
+
+ state->clock_freq = audiofreq;
+ /* de-activate */
+ wm8739_write(sd, R9, 0x000);
+ switch (audiofreq) {
+ case 44100:
+ /* 256fps, fs=44.1k */
+ wm8739_write(sd, R8, 0x020);
+ break;
+ case 48000:
+ /* 256fps, fs=48k */
+ wm8739_write(sd, R8, 0x000);
+ break;
+ case 32000:
+ /* 256fps, fs=32k */
+ wm8739_write(sd, R8, 0x018);
+ break;
+ default:
break;
}
+ /* activate */
+ wm8739_write(sd, R9, 0x001);
+ return 0;
+}
- case VIDIOC_G_CTRL:
- return wm8739_get_ctrl(client, arg);
-
- case VIDIOC_S_CTRL:
- return wm8739_set_ctrl(client, arg);
+static int wm8739_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ int i;
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++)
- if (qc->id && qc->id == wm8739_qctrl[i].id) {
- memcpy(qc, &wm8739_qctrl[i], sizeof(*qc));
- return 0;
- }
- return -EINVAL;
- }
+ for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++)
+ if (qc->id && qc->id == wm8739_qctrl[i].id) {
+ memcpy(qc, &wm8739_qctrl[i], sizeof(*qc));
+ return 0;
+ }
+ return -EINVAL;
+}
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client,
- arg, V4L2_IDENT_WM8739, 0);
+static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Frequency: %u Hz\n", state->clock_freq);
- v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f,
- state->muted ? " (muted)" : "");
- v4l_info(client, "Volume R: %02x%s\n", state->vol_r & 0x1f,
- state->muted ? " (muted)" : "");
- break;
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0);
+}
- default:
- return -EINVAL;
- }
+static int wm8739_log_status(struct v4l2_subdev *sd)
+{
+ struct wm8739_state *state = to_state(sd);
+ v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq);
+ v4l2_info(sd, "Volume L: %02x%s\n", state->vol_l & 0x1f,
+ state->muted ? " (muted)" : "");
+ v4l2_info(sd, "Volume R: %02x%s\n", state->vol_r & 0x1f,
+ state->muted ? " (muted)" : "");
return 0;
}
+static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops wm8739_core_ops = {
+ .log_status = wm8739_log_status,
+ .g_chip_ident = wm8739_g_chip_ident,
+ .queryctrl = wm8739_queryctrl,
+ .g_ctrl = wm8739_g_ctrl,
+ .s_ctrl = wm8739_s_ctrl,
+};
+
+static const struct v4l2_subdev_audio_ops wm8739_audio_ops = {
+ .s_clock_freq = wm8739_s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops wm8739_ops = {
+ .core = &wm8739_core_ops,
+ .audio = &wm8739_audio_ops,
+};
+
/* ------------------------------------------------------------------------ */
/* i2c implementation */
@@ -265,6 +284,7 @@ static int wm8739_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct wm8739_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -276,6 +296,8 @@ static int wm8739_probe(struct i2c_client *client,
state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &wm8739_ops);
state->vol_l = 0x17; /* 0dB */
state->vol_r = 0x17; /* 0dB */
state->muted = 0;
@@ -283,31 +305,33 @@ static int wm8739_probe(struct i2c_client *client,
/* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */
state->volume = ((long)state->vol_l + 1) * 65535 / 31;
state->clock_freq = 48000;
- i2c_set_clientdata(client, state);
/* Initialize wm8739 */
/* reset */
- wm8739_write(client, R15, 0x00);
+ wm8739_write(sd, R15, 0x00);
/* filter setting, high path, offet clear */
- wm8739_write(client, R5, 0x000);
+ wm8739_write(sd, R5, 0x000);
/* ADC, OSC, Power Off mode Disable */
- wm8739_write(client, R6, 0x000);
+ wm8739_write(sd, R6, 0x000);
/* Digital Audio interface format:
Enable Master mode, 24 bit, MSB first/left justified */
- wm8739_write(client, R7, 0x049);
+ wm8739_write(sd, R7, 0x049);
/* sampling control: normal, 256fs, 48KHz sampling rate */
- wm8739_write(client, R8, 0x000);
+ wm8739_write(sd, R8, 0x000);
/* activate */
- wm8739_write(client, R9, 0x001);
+ wm8739_write(sd, R9, 0x001);
/* set volume/mute */
- wm8739_set_audio(client);
+ wm8739_set_audio(sd);
return 0;
}
static int wm8739_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
index 48df661d4fc3..d0220b0ec0bc 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/video/wm8775.c
@@ -32,7 +32,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv-legacy.h>
@@ -54,16 +54,23 @@ enum {
};
struct wm8775_state {
+ struct v4l2_subdev sd;
u8 input; /* Last selected input (0-0xf) */
u8 muted;
};
-static int wm8775_write(struct i2c_client *client, int reg, u16 val)
+static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
{
+ return container_of(sd, struct wm8775_state, sd);
+}
+
+static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
if (reg < 0 || reg >= TOT_REGS) {
- v4l_err(client, "Invalid register R%d\n", reg);
+ v4l2_err(sd, "Invalid register R%d\n", reg);
return -1;
}
@@ -71,84 +78,117 @@ static int wm8775_write(struct i2c_client *client, int reg, u16 val)
if (i2c_smbus_write_byte_data(client,
(reg << 1) | (val >> 8), val & 0xff) == 0)
return 0;
- v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
+ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
return -1;
}
-static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int wm8775_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct wm8775_state *state = i2c_get_clientdata(client);
- struct v4l2_routing *route = arg;
- struct v4l2_control *ctrl = arg;
-
- switch (cmd) {
- case VIDIOC_INT_G_AUDIO_ROUTING:
- route->input = state->input;
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- /* There are 4 inputs and one output. Zero or more inputs
- are multiplexed together to the output. Hence there are
- 16 combinations.
- If only one input is active (the normal case) then the
- input values 1, 2, 4 or 8 should be used. */
- if (route->input > 15) {
- v4l_err(client, "Invalid input %d.\n", route->input);
- return -EINVAL;
- }
- state->input = route->input;
- if (state->muted)
- break;
- wm8775_write(client, R21, 0x0c0);
- wm8775_write(client, R14, 0x1d4);
- wm8775_write(client, R15, 0x1d4);
- wm8775_write(client, R21, 0x100 + state->input);
- break;
-
- case VIDIOC_G_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- ctrl->value = state->muted;
- break;
-
- case VIDIOC_S_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- state->muted = ctrl->value;
- wm8775_write(client, R21, 0x0c0);
- wm8775_write(client, R14, 0x1d4);
- wm8775_write(client, R15, 0x1d4);
- if (!state->muted)
- wm8775_write(client, R21, 0x100 + state->input);
- break;
-
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client,
- arg, V4L2_IDENT_WM8775, 0);
-
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Input: %d%s\n", state->input,
- state->muted ? " (muted)" : "");
- break;
-
- case VIDIOC_S_FREQUENCY:
- /* If I remove this, then it can happen that I have no
- sound the first time I tune from static to a valid channel.
- It's difficult to reproduce and is almost certainly related
- to the zero cross detect circuit. */
- wm8775_write(client, R21, 0x0c0);
- wm8775_write(client, R14, 0x1d4);
- wm8775_write(client, R15, 0x1d4);
- wm8775_write(client, R21, 0x100 + state->input);
- break;
-
- default:
+ struct wm8775_state *state = to_state(sd);
+
+ /* There are 4 inputs and one output. Zero or more inputs
+ are multiplexed together to the output. Hence there are
+ 16 combinations.
+ If only one input is active (the normal case) then the
+ input values 1, 2, 4 or 8 should be used. */
+ if (route->input > 15) {
+ v4l2_err(sd, "Invalid input %d.\n", route->input);
return -EINVAL;
}
+ state->input = route->input;
+ if (state->muted)
+ return 0;
+ wm8775_write(sd, R21, 0x0c0);
+ wm8775_write(sd, R14, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
+ wm8775_write(sd, R21, 0x100 + state->input);
+ return 0;
+}
+
+static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ ctrl->value = state->muted;
+ return 0;
+}
+
+static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ state->muted = ctrl->value;
+ wm8775_write(sd, R21, 0x0c0);
+ wm8775_write(sd, R14, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
+ if (!state->muted)
+ wm8775_write(sd, R21, 0x100 + state->input);
+ return 0;
+}
+
+static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0);
+}
+
+static int wm8775_log_status(struct v4l2_subdev *sd)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ v4l2_info(sd, "Input: %d%s\n", state->input,
+ state->muted ? " (muted)" : "");
return 0;
}
+static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ /* If I remove this, then it can happen that I have no
+ sound the first time I tune from static to a valid channel.
+ It's difficult to reproduce and is almost certainly related
+ to the zero cross detect circuit. */
+ wm8775_write(sd, R21, 0x0c0);
+ wm8775_write(sd, R14, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
+ wm8775_write(sd, R21, 0x100 + state->input);
+ return 0;
+}
+
+static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops wm8775_core_ops = {
+ .log_status = wm8775_log_status,
+ .g_chip_ident = wm8775_g_chip_ident,
+ .g_ctrl = wm8775_g_ctrl,
+ .s_ctrl = wm8775_s_ctrl,
+};
+
+static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
+ .s_frequency = wm8775_s_frequency,
+};
+
+static const struct v4l2_subdev_audio_ops wm8775_audio_ops = {
+ .s_routing = wm8775_s_routing,
+};
+
+static const struct v4l2_subdev_ops wm8775_ops = {
+ .core = &wm8775_core_ops,
+ .tuner = &wm8775_tuner_ops,
+ .audio = &wm8775_audio_ops,
+};
+
/* ----------------------------------------------------------------------- */
/* i2c implementation */
@@ -162,56 +202,61 @@ static int wm8775_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct wm8775_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- v4l_info(client, "chip found @ 0x%x (%s)\n",
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
state->input = 2;
state->muted = 0;
- i2c_set_clientdata(client, state);
/* Initialize wm8775 */
/* RESET */
- wm8775_write(client, R23, 0x000);
+ wm8775_write(sd, R23, 0x000);
/* Disable zero cross detect timeout */
- wm8775_write(client, R7, 0x000);
+ wm8775_write(sd, R7, 0x000);
/* Left justified, 24-bit mode */
- wm8775_write(client, R11, 0x021);
+ wm8775_write(sd, R11, 0x021);
/* Master mode, clock ratio 256fs */
- wm8775_write(client, R12, 0x102);
+ wm8775_write(sd, R12, 0x102);
/* Powered up */
- wm8775_write(client, R13, 0x000);
+ wm8775_write(sd, R13, 0x000);
/* ADC gain +2.5dB, enable zero cross */
- wm8775_write(client, R14, 0x1d4);
+ wm8775_write(sd, R14, 0x1d4);
/* ADC gain +2.5dB, enable zero cross */
- wm8775_write(client, R15, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
/* ALC Stereo, ALC target level -1dB FS max gain +8dB */
- wm8775_write(client, R16, 0x1bf);
+ wm8775_write(sd, R16, 0x1bf);
/* Enable gain control, use zero cross detection,
ALC hold time 42.6 ms */
- wm8775_write(client, R17, 0x185);
+ wm8775_write(sd, R17, 0x185);
/* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */
- wm8775_write(client, R18, 0x0a2);
+ wm8775_write(sd, R18, 0x0a2);
/* Enable noise gate, threshold -72dBfs */
- wm8775_write(client, R19, 0x005);
+ wm8775_write(sd, R19, 0x005);
/* Transient window 4ms, lower PGA gain limit -1dB */
- wm8775_write(client, R20, 0x07a);
+ wm8775_write(sd, R20, 0x07a);
/* LRBOTH = 1, use input 2. */
- wm8775_write(client, R21, 0x102);
+ wm8775_write(sd, R21, 0x102);
return 0;
}
static int wm8775_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c
index 9fc581707638..9d00e6056491 100644
--- a/drivers/media/video/zc0301/zc0301_core.c
+++ b/drivers/media/video/zc0301/zc0301_core.c
@@ -1020,7 +1020,7 @@ zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg)
strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
- strlcpy(cap.bus_info, cam->usbdev->dev.bus_id,
+ strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev),
sizeof(cap.bus_info));
if (copy_to_user(arg, &cap, sizeof(cap)))
diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c
index fa5f2f8f518a..05f39195372e 100644
--- a/drivers/media/video/zoran/zoran_card.c
+++ b/drivers/media/video/zoran/zoran_card.c
@@ -153,12 +153,6 @@ MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver");
MODULE_AUTHOR("Serguei Miridonov");
MODULE_LICENSE("GPL");
-static struct pci_device_id zr36067_pci_tbl[] = {
- {PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {0}
-};
-MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl);
int zoran_num; /* number of Buzs in use */
struct zoran *zoran[BUZ_MAX];
diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
index db11ab9e60da..00b97d97aeaa 100644
--- a/drivers/media/video/zoran/zoran_driver.c
+++ b/drivers/media/video/zoran/zoran_driver.c
@@ -1940,11 +1940,7 @@ zoran_set_input (struct zoran *zr,
* ioctl routine
*/
-static int
-zoran_do_ioctl (struct inode *inode,
- struct file *file,
- unsigned int cmd,
- void *arg)
+static int zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct zoran_fh *fh = file->private_data;
struct zoran *zr = fh->zr;
@@ -4201,7 +4197,7 @@ zoran_ioctl (struct inode *inode,
unsigned int cmd,
unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, zoran_do_ioctl);
+ return video_usercopy(file, cmd, arg, zoran_do_ioctl);
}
static unsigned int
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index d6a0074b9dc3..c4e8b9aa3827 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -952,7 +952,6 @@ mpt_put_msg_frame_hi_pri(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* mpt_free_msg_frame - Place MPT request frame back on FreeQ.
- * @handle: Handle of registered MPT protocol driver
* @ioc: Pointer to MPT adapter structure
* @mf: Pointer to MPT request frame
*
@@ -4563,7 +4562,7 @@ WaitForDoorbellReply(MPT_ADAPTER *ioc, int howlong, int sleepFlag)
failcnt++;
hword = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF);
/* don't overflow our IOC hs_reply[] buffer! */
- if (u16cnt < sizeof(ioc->hs_reply) / sizeof(ioc->hs_reply[0]))
+ if (u16cnt < ARRAY_SIZE(ioc->hs_reply))
hs_reply[u16cnt] = hword;
CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
}
@@ -5422,7 +5421,6 @@ mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhysDiskPage0_t
/**
* mpt_findImVolumes - Identify IDs of hidden disks and RAID Volumes
* @ioc: Pointer to a Adapter Strucutre
- * @portnum: IOC port number
*
* Return:
* 0 on success
@@ -6939,7 +6937,6 @@ mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info)
/**
* mpt_spi_log_info - Log information returned from SCSI Parallel IOC.
* @ioc: Pointer to MPT_ADAPTER structure
- * @mr: Pointer to MPT reply frame
* @log_info: U32 LogInfo word from the IOC
*
* Refer to lsi/sp_log.h.
@@ -7176,7 +7173,7 @@ union loginfo_type {
sas_loginfo.loginfo = log_info;
if ((sas_loginfo.dw.bus_type != 3 /*SAS*/) &&
- (sas_loginfo.dw.originator < sizeof(originator_str)/sizeof(char*)))
+ (sas_loginfo.dw.originator < ARRAY_SIZE(originator_str)))
return;
originator_desc = originator_str[sas_loginfo.dw.originator];
@@ -7185,21 +7182,21 @@ union loginfo_type {
case 0: /* IOP */
if (sas_loginfo.dw.code <
- sizeof(iop_code_str)/sizeof(char*))
+ ARRAY_SIZE(iop_code_str))
code_desc = iop_code_str[sas_loginfo.dw.code];
break;
case 1: /* PL */
if (sas_loginfo.dw.code <
- sizeof(pl_code_str)/sizeof(char*))
+ ARRAY_SIZE(pl_code_str))
code_desc = pl_code_str[sas_loginfo.dw.code];
break;
case 2: /* IR */
if (sas_loginfo.dw.code >=
- sizeof(ir_code_str)/sizeof(char*))
+ ARRAY_SIZE(ir_code_str))
break;
code_desc = ir_code_str[sas_loginfo.dw.code];
if (sas_loginfo.dw.subcode >=
- sizeof(raid_sub_code_str)/sizeof(char*))
+ ARRAY_SIZE(raid_sub_code_str))
break;
if (sas_loginfo.dw.code == 0)
sub_code_desc =
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index e4c0db4dc7b1..9e485459f63b 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -474,9 +474,9 @@ static __init int asic3_gpio_probe(struct platform_device *pdev,
u16 dir_reg[ASIC3_NUM_GPIO_BANKS];
int i;
- memzero(alt_reg, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
- memzero(out_reg, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
- memzero(dir_reg, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
+ memset(alt_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
+ memset(out_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
+ memset(dir_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
/* Enable all GPIOs */
asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, MASK), 0xffff);
diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c
index b4ed57e02729..6063dc2b52e8 100644
--- a/drivers/mfd/mcp-core.c
+++ b/drivers/mfd/mcp-core.c
@@ -18,7 +18,7 @@
#include <linux/slab.h>
#include <linux/string.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <asm/system.h>
#include "mcp.h"
diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c
index 28380b20bc70..62b32dabf629 100644
--- a/drivers/mfd/mcp-sa11x0.c
+++ b/drivers/mfd/mcp-sa11x0.c
@@ -20,7 +20,7 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/system.h>
diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c
index 61aeaf79640d..86fed4870f93 100644
--- a/drivers/mfd/ucb1x00-assabet.c
+++ b/drivers/mfd/ucb1x00-assabet.c
@@ -15,7 +15,7 @@
#include <linux/proc_fs.h>
#include <linux/device.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include "ucb1x00.h"
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c
index a316f1b75933..6860c924f364 100644
--- a/drivers/mfd/ucb1x00-core.c
+++ b/drivers/mfd/ucb1x00-core.c
@@ -25,7 +25,7 @@
#include <linux/device.h>
#include <linux/mutex.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/hardware.h>
#include "ucb1x00.h"
diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c
index 44762ca86a8d..61b7d3eb9a2f 100644
--- a/drivers/mfd/ucb1x00-ts.c
+++ b/drivers/mfd/ucb1x00-ts.c
@@ -31,7 +31,7 @@
#include <linux/slab.h>
#include <linux/kthread.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/collie.h>
#include <asm/mach-types.h>
diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c
index 2f0fcdb869b7..eb29b1d933ac 100644
--- a/drivers/mmc/host/imxmmc.c
+++ b/drivers/mmc/host/imxmmc.c
@@ -10,20 +10,6 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * 2005-04-17 Pavel Pisa <pisa@cmp.felk.cvut.cz>
- * Changed to conform redesigned i.MX scatter gather DMA interface
- *
- * 2005-11-04 Pavel Pisa <pisa@cmp.felk.cvut.cz>
- * Updated for 2.6.14 kernel
- *
- * 2005-12-13 Jay Monkman <jtm@smoothsmoothie.com>
- * Found and corrected problems in the write path
- *
- * 2005-12-30 Pavel Pisa <pisa@cmp.felk.cvut.cz>
- * The event handling rewritten right way in softirq.
- * Added many ugly hacks and delays to overcome SDHC
- * deficiencies
- *
*/
#include <linux/module.h>
@@ -37,9 +23,9 @@
#include <linux/mmc/card.h>
#include <linux/delay.h>
#include <linux/clk.h>
+#include <linux/io.h>
#include <asm/dma.h>
-#include <asm/io.h>
#include <asm/irq.h>
#include <asm/sizes.h>
#include <mach/mmc.h>
@@ -50,17 +36,16 @@
#define DRIVER_NAME "imx-mmc"
#define IMXMCI_INT_MASK_DEFAULT (INT_MASK_BUF_READY | INT_MASK_DATA_TRAN | \
- INT_MASK_WRITE_OP_DONE | INT_MASK_END_CMD_RES | \
- INT_MASK_AUTO_CARD_DETECT | INT_MASK_DAT0_EN | INT_MASK_SDIO)
+ INT_MASK_WRITE_OP_DONE | INT_MASK_END_CMD_RES | \
+ INT_MASK_AUTO_CARD_DETECT | INT_MASK_DAT0_EN | INT_MASK_SDIO)
struct imxmci_host {
struct mmc_host *mmc;
spinlock_t lock;
struct resource *res;
+ void __iomem *base;
int irq;
imx_dmach_t dma;
- unsigned int clkrt;
- unsigned int cmdat;
volatile unsigned int imask;
unsigned int power_mode;
unsigned int present;
@@ -74,7 +59,7 @@ struct imxmci_host {
struct tasklet_struct tasklet;
unsigned int status_reg;
unsigned long pending_events;
- /* Next to fields are there for CPU driven transfers to overcome SDHC deficiencies */
+ /* Next two fields are there for CPU driven transfers to overcome SDHC deficiencies */
u16 *data_ptr;
unsigned int data_cnt;
atomic_t stuck_timeout;
@@ -114,14 +99,22 @@ struct imxmci_host {
static void imxmci_stop_clock(struct imxmci_host *host)
{
int i = 0;
- MMC_STR_STP_CLK &= ~STR_STP_CLK_START_CLK;
- while(i < 0x1000) {
- if(!(i & 0x7f))
- MMC_STR_STP_CLK |= STR_STP_CLK_STOP_CLK;
+ u16 reg;
+
+ reg = readw(host->base + MMC_REG_STR_STP_CLK);
+ writew(reg & ~STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
+ while (i < 0x1000) {
+ if (!(i & 0x7f)) {
+ reg = readw(host->base + MMC_REG_STR_STP_CLK);
+ writew(reg | STR_STP_CLK_STOP_CLK,
+ host->base + MMC_REG_STR_STP_CLK);
+ }
- if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)) {
+ reg = readw(host->base + MMC_REG_STATUS);
+ if (!(reg & STATUS_CARD_BUS_CLK_RUN)) {
/* Check twice before cut */
- if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN))
+ reg = readw(host->base + MMC_REG_STATUS);
+ if (!(reg & STATUS_CARD_BUS_CLK_RUN))
return;
}
@@ -135,8 +128,10 @@ static int imxmci_start_clock(struct imxmci_host *host)
unsigned int trials = 0;
unsigned int delay_limit = 128;
unsigned long flags;
+ u16 reg;
- MMC_STR_STP_CLK &= ~STR_STP_CLK_STOP_CLK;
+ reg = readw(host->base + MMC_REG_STR_STP_CLK);
+ writew(reg & ~STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
clear_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
@@ -145,18 +140,21 @@ static int imxmci_start_clock(struct imxmci_host *host)
* then 6 delay loops, but during card detection (low clockrate)
* it takes up to 5000 delay loops and sometimes fails for the first time
*/
- MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
+ reg = readw(host->base + MMC_REG_STR_STP_CLK);
+ writew(reg | STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
do {
unsigned int delay = delay_limit;
- while(delay--){
- if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
+ while (delay--) {
+ reg = readw(host->base + MMC_REG_STATUS);
+ if (reg & STATUS_CARD_BUS_CLK_RUN)
/* Check twice before cut */
- if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
+ reg = readw(host->base + MMC_REG_STATUS);
+ if (reg & STATUS_CARD_BUS_CLK_RUN)
return 0;
- if(test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
+ if (test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
return 0;
}
@@ -167,58 +165,59 @@ static int imxmci_start_clock(struct imxmci_host *host)
* IRQ or schedule delays this function execution and the clocks has
* been already stopped by other means (response processing, SDHC HW)
*/
- if(!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
- MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
+ if (!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) {
+ reg = readw(host->base + MMC_REG_STR_STP_CLK);
+ writew(reg | STR_STP_CLK_START_CLK,
+ host->base + MMC_REG_STR_STP_CLK);
+ }
local_irq_restore(flags);
- } while(++trials<256);
+ } while (++trials < 256);
dev_err(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n");
return -1;
}
-static void imxmci_softreset(void)
+static void imxmci_softreset(struct imxmci_host *host)
{
+ int i;
+
/* reset sequence */
- MMC_STR_STP_CLK = 0x8;
- MMC_STR_STP_CLK = 0xD;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
-
- MMC_RES_TO = 0xff;
- MMC_BLK_LEN = 512;
- MMC_NOB = 1;
+ writew(0x08, host->base + MMC_REG_STR_STP_CLK);
+ writew(0x0D, host->base + MMC_REG_STR_STP_CLK);
+
+ for (i = 0; i < 8; i++)
+ writew(0x05, host->base + MMC_REG_STR_STP_CLK);
+
+ writew(0xff, host->base + MMC_REG_RES_TO);
+ writew(512, host->base + MMC_REG_BLK_LEN);
+ writew(1, host->base + MMC_REG_NOB);
}
static int imxmci_busy_wait_for_status(struct imxmci_host *host,
- unsigned int *pstat, unsigned int stat_mask,
- int timeout, const char *where)
+ unsigned int *pstat, unsigned int stat_mask,
+ int timeout, const char *where)
{
- int loops=0;
- while(!(*pstat & stat_mask)) {
- loops+=2;
- if(loops >= timeout) {
+ int loops = 0;
+
+ while (!(*pstat & stat_mask)) {
+ loops += 2;
+ if (loops >= timeout) {
dev_dbg(mmc_dev(host->mmc), "busy wait timeout in %s, STATUS = 0x%x (0x%x)\n",
where, *pstat, stat_mask);
return -1;
}
udelay(2);
- *pstat |= MMC_STATUS;
+ *pstat |= readw(host->base + MMC_REG_STATUS);
}
- if(!loops)
+ if (!loops)
return 0;
/* The busy-wait is expected there for clock <8MHz due to SDHC hardware flaws */
- if(!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock>=8000000))
+ if (!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock >= 8000000))
dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n",
- loops, where, *pstat, stat_mask);
+ loops, where, *pstat, stat_mask);
return loops;
}
@@ -235,8 +234,8 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
host->data = data;
data->bytes_xfered = 0;
- MMC_NOB = nob;
- MMC_BLK_LEN = blksz;
+ writew(nob, host->base + MMC_REG_NOB);
+ writew(blksz, host->base + MMC_REG_BLK_LEN);
/*
* DMA cannot be used for small block sizes, we have to use CPU driven transfers otherwise.
@@ -252,14 +251,14 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
host->dma_dir = DMA_FROM_DEVICE;
/* Hack to enable read SCR */
- MMC_NOB = 1;
- MMC_BLK_LEN = 512;
+ writew(1, host->base + MMC_REG_NOB);
+ writew(512, host->base + MMC_REG_BLK_LEN);
} else {
host->dma_dir = DMA_TO_DEVICE;
}
/* Convert back to virtual address */
- host->data_ptr = (u16*)sg_virt(data->sg);
+ host->data_ptr = (u16 *)sg_virt(data->sg);
host->data_cnt = 0;
clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events);
@@ -271,10 +270,11 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
if (data->flags & MMC_DATA_READ) {
host->dma_dir = DMA_FROM_DEVICE;
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, host->dma_dir);
+ data->sg_len, host->dma_dir);
imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
- host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_READ);
+ host->res->start + MMC_REG_BUFFER_ACCESS,
+ DMA_MODE_READ);
/*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_READ, IMX_DMA_WIDTH_16, CCR_REN);*/
CCR(host->dma) = CCR_DMOD_LINEAR | CCR_DSIZ_32 | CCR_SMOD_FIFO | CCR_SSIZ_16 | CCR_REN;
@@ -282,10 +282,11 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
host->dma_dir = DMA_TO_DEVICE;
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, host->dma_dir);
+ data->sg_len, host->dma_dir);
imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
- host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_WRITE);
+ host->res->start + MMC_REG_BUFFER_ACCESS,
+ DMA_MODE_WRITE);
/*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_WRITE, IMX_DMA_WIDTH_16, CCR_REN);*/
CCR(host->dma) = CCR_SMOD_LINEAR | CCR_SSIZ_32 | CCR_DMOD_FIFO | CCR_DSIZ_16 | CCR_REN;
@@ -293,12 +294,12 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
#if 1 /* This code is there only for consistency checking and can be disabled in future */
host->dma_size = 0;
- for(i=0; i<host->dma_nents; i++)
- host->dma_size+=data->sg[i].length;
+ for (i = 0; i < host->dma_nents; i++)
+ host->dma_size += data->sg[i].length;
if (datasz > host->dma_size) {
dev_err(mmc_dev(host->mmc), "imxmci_setup_data datasz 0x%x > 0x%x dm_size\n",
- datasz, host->dma_size);
+ datasz, host->dma_size);
}
#endif
@@ -306,7 +307,7 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
wmb();
- if(host->actual_bus_width == MMC_BUS_WIDTH_4)
+ if (host->actual_bus_width == MMC_BUS_WIDTH_4)
BLR(host->dma) = 0; /* burst 64 byte read / 64 bytes write */
else
BLR(host->dma) = 16; /* burst 16 byte read / 16 bytes write */
@@ -317,9 +318,8 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
clear_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events);
/* start DMA engine for read, write is delayed after initial response */
- if (host->dma_dir == DMA_FROM_DEVICE) {
+ if (host->dma_dir == DMA_FROM_DEVICE)
imx_dma_enable(host->dma);
- }
}
static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd, unsigned int cmdat)
@@ -351,16 +351,16 @@ static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd,
break;
}
- if ( test_and_clear_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events) )
+ if (test_and_clear_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events))
cmdat |= CMD_DAT_CONT_INIT; /* This command needs init */
- if ( host->actual_bus_width == MMC_BUS_WIDTH_4 )
+ if (host->actual_bus_width == MMC_BUS_WIDTH_4)
cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
- MMC_CMD = cmd->opcode;
- MMC_ARGH = cmd->arg >> 16;
- MMC_ARGL = cmd->arg & 0xffff;
- MMC_CMD_DAT_CONT = cmdat;
+ writew(cmd->opcode, host->base + MMC_REG_CMD);
+ writew(cmd->arg >> 16, host->base + MMC_REG_ARGH);
+ writew(cmd->arg & 0xffff, host->base + MMC_REG_ARGL);
+ writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT);
atomic_set(&host->stuck_timeout, 0);
set_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events);
@@ -368,18 +368,18 @@ static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd,
imask = IMXMCI_INT_MASK_DEFAULT;
imask &= ~INT_MASK_END_CMD_RES;
- if ( cmdat & CMD_DAT_CONT_DATA_ENABLE ) {
- /*imask &= ~INT_MASK_BUF_READY;*/
+ if (cmdat & CMD_DAT_CONT_DATA_ENABLE) {
+ /* imask &= ~INT_MASK_BUF_READY; */
imask &= ~INT_MASK_DATA_TRAN;
- if ( cmdat & CMD_DAT_CONT_WRITE )
+ if (cmdat & CMD_DAT_CONT_WRITE)
imask &= ~INT_MASK_WRITE_OP_DONE;
- if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
+ if (test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
imask &= ~INT_MASK_BUF_READY;
}
spin_lock_irqsave(&host->lock, flags);
host->imask = imask;
- MMC_INT_MASK = host->imask;
+ writew(host->imask, host->base + MMC_REG_INT_MASK);
spin_unlock_irqrestore(&host->lock, flags);
dev_dbg(mmc_dev(host->mmc), "CMD%02d (0x%02x) mask set to 0x%04x\n",
@@ -395,14 +395,14 @@ static void imxmci_finish_request(struct imxmci_host *host, struct mmc_request *
spin_lock_irqsave(&host->lock, flags);
host->pending_events &= ~(IMXMCI_PEND_WAIT_RESP_m | IMXMCI_PEND_DMA_END_m |
- IMXMCI_PEND_DMA_DATA_m | IMXMCI_PEND_CPU_DATA_m);
+ IMXMCI_PEND_DMA_DATA_m | IMXMCI_PEND_CPU_DATA_m);
host->imask = IMXMCI_INT_MASK_DEFAULT;
- MMC_INT_MASK = host->imask;
+ writew(host->imask, host->base + MMC_REG_INT_MASK);
spin_unlock_irqrestore(&host->lock, flags);
- if(req && req->cmd)
+ if (req && req->cmd)
host->prev_cmd_code = req->cmd->opcode;
host->req = NULL;
@@ -416,17 +416,17 @@ static int imxmci_finish_data(struct imxmci_host *host, unsigned int stat)
struct mmc_data *data = host->data;
int data_error;
- if(test_and_clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)){
+ if (test_and_clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
imx_dma_disable(host->dma);
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents,
host->dma_dir);
}
- if ( stat & STATUS_ERR_MASK ) {
- dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat);
- if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR))
+ if (stat & STATUS_ERR_MASK) {
+ dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n", stat);
+ if (stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR))
data->error = -EILSEQ;
- else if(stat & STATUS_TIME_OUT_READ)
+ else if (stat & STATUS_TIME_OUT_READ)
data->error = -ETIMEDOUT;
else
data->error = -EIO;
@@ -445,7 +445,7 @@ static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
{
struct mmc_command *cmd = host->cmd;
int i;
- u32 a,b,c;
+ u32 a, b, c;
struct mmc_data *data = host->data;
if (!cmd)
@@ -461,18 +461,18 @@ static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
cmd->error = -EILSEQ;
}
- if(cmd->flags & MMC_RSP_PRESENT) {
- if(cmd->flags & MMC_RSP_136) {
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
for (i = 0; i < 4; i++) {
- u32 a = MMC_RES_FIFO & 0xffff;
- u32 b = MMC_RES_FIFO & 0xffff;
- cmd->resp[i] = a<<16 | b;
+ a = readw(host->base + MMC_REG_RES_FIFO);
+ b = readw(host->base + MMC_REG_RES_FIFO);
+ cmd->resp[i] = a << 16 | b;
}
} else {
- a = MMC_RES_FIFO & 0xffff;
- b = MMC_RES_FIFO & 0xffff;
- c = MMC_RES_FIFO & 0xffff;
- cmd->resp[0] = a<<24 | b<<8 | c>>8;
+ a = readw(host->base + MMC_REG_RES_FIFO);
+ b = readw(host->base + MMC_REG_RES_FIFO);
+ c = readw(host->base + MMC_REG_RES_FIFO);
+ cmd->resp[0] = a << 24 | b << 8 | c >> 8;
}
}
@@ -484,36 +484,34 @@ static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
/* Wait for FIFO to be empty before starting DMA write */
- stat = MMC_STATUS;
- if(imxmci_busy_wait_for_status(host, &stat,
- STATUS_APPL_BUFF_FE,
- 40, "imxmci_cmd_done DMA WR") < 0) {
+ stat = readw(host->base + MMC_REG_STATUS);
+ if (imxmci_busy_wait_for_status(host, &stat,
+ STATUS_APPL_BUFF_FE,
+ 40, "imxmci_cmd_done DMA WR") < 0) {
cmd->error = -EIO;
imxmci_finish_data(host, stat);
- if(host->req)
+ if (host->req)
imxmci_finish_request(host, host->req);
dev_warn(mmc_dev(host->mmc), "STATUS = 0x%04x\n",
- stat);
+ stat);
return 0;
}
- if(test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
+ if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
imx_dma_enable(host->dma);
- }
}
} else {
struct mmc_request *req;
imxmci_stop_clock(host);
req = host->req;
- if(data)
+ if (data)
imxmci_finish_data(host, stat);
- if( req ) {
+ if (req)
imxmci_finish_request(host, req);
- } else {
+ else
dev_warn(mmc_dev(host->mmc), "imxmci_cmd_done: no request to finish\n");
- }
}
return 1;
@@ -535,11 +533,10 @@ static int imxmci_data_done(struct imxmci_host *host, unsigned int stat)
} else {
struct mmc_request *req;
req = host->req;
- if( req ) {
+ if (req)
imxmci_finish_request(host, req);
- } else {
+ else
dev_warn(mmc_dev(host->mmc), "imxmci_data_done: no request to finish\n");
- }
}
return 1;
@@ -552,7 +549,7 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
int trans_done = 0;
unsigned int stat = *pstat;
- if(host->actual_bus_width != MMC_BUS_WIDTH_4)
+ if (host->actual_bus_width != MMC_BUS_WIDTH_4)
burst_len = 16;
else
burst_len = 64;
@@ -563,44 +560,44 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
udelay(20); /* required for clocks < 8MHz*/
- if(host->dma_dir == DMA_FROM_DEVICE) {
+ if (host->dma_dir == DMA_FROM_DEVICE) {
imxmci_busy_wait_for_status(host, &stat,
- STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE |
- STATUS_TIME_OUT_READ,
- 50, "imxmci_cpu_driven_data read");
+ STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE |
+ STATUS_TIME_OUT_READ,
+ 50, "imxmci_cpu_driven_data read");
- while((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) &&
- !(stat & STATUS_TIME_OUT_READ) &&
- (host->data_cnt < 512)) {
+ while ((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) &&
+ !(stat & STATUS_TIME_OUT_READ) &&
+ (host->data_cnt < 512)) {
udelay(20); /* required for clocks < 8MHz*/
- for(i = burst_len; i>=2 ; i-=2) {
+ for (i = burst_len; i >= 2 ; i -= 2) {
u16 data;
- data = MMC_BUFFER_ACCESS;
+ data = readw(host->base + MMC_REG_BUFFER_ACCESS);
udelay(10); /* required for clocks < 8MHz*/
- if(host->data_cnt+2 <= host->dma_size) {
+ if (host->data_cnt+2 <= host->dma_size) {
*(host->data_ptr++) = data;
} else {
- if(host->data_cnt < host->dma_size)
- *(u8*)(host->data_ptr) = data;
+ if (host->data_cnt < host->dma_size)
+ *(u8 *)(host->data_ptr) = data;
}
host->data_cnt += 2;
}
- stat = MMC_STATUS;
+ stat = readw(host->base + MMC_REG_STATUS);
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read %d burst %d STATUS = 0x%x\n",
host->data_cnt, burst_len, stat);
}
- if((stat & STATUS_DATA_TRANS_DONE) && (host->data_cnt >= 512))
+ if ((stat & STATUS_DATA_TRANS_DONE) && (host->data_cnt >= 512))
trans_done = 1;
- if(host->dma_size & 0x1ff)
+ if (host->dma_size & 0x1ff)
stat &= ~STATUS_CRC_READ_ERR;
- if(stat & STATUS_TIME_OUT_READ) {
+ if (stat & STATUS_TIME_OUT_READ) {
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read timeout STATUS = 0x%x\n",
stat);
trans_done = -1;
@@ -608,12 +605,12 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
} else {
imxmci_busy_wait_for_status(host, &stat,
- STATUS_APPL_BUFF_FE,
- 20, "imxmci_cpu_driven_data write");
+ STATUS_APPL_BUFF_FE,
+ 20, "imxmci_cpu_driven_data write");
- while((stat & STATUS_APPL_BUFF_FE) &&
- (host->data_cnt < host->dma_size)) {
- if(burst_len >= host->dma_size - host->data_cnt) {
+ while ((stat & STATUS_APPL_BUFF_FE) &&
+ (host->data_cnt < host->dma_size)) {
+ if (burst_len >= host->dma_size - host->data_cnt) {
burst_len = host->dma_size - host->data_cnt;
host->data_cnt = host->dma_size;
trans_done = 1;
@@ -621,10 +618,10 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
host->data_cnt += burst_len;
}
- for(i = burst_len; i>0 ; i-=2)
- MMC_BUFFER_ACCESS = *(host->data_ptr++);
+ for (i = burst_len; i > 0 ; i -= 2)
+ writew(*(host->data_ptr++), host->base + MMC_REG_BUFFER_ACCESS);
- stat = MMC_STATUS;
+ stat = readw(host->base + MMC_REG_STATUS);
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data write burst %d STATUS = 0x%x\n",
burst_len, stat);
@@ -639,7 +636,7 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
static void imxmci_dma_irq(int dma, void *devid)
{
struct imxmci_host *host = devid;
- uint32_t stat = MMC_STATUS;
+ u32 stat = readw(host->base + MMC_REG_STATUS);
atomic_set(&host->stuck_timeout, 0);
host->status_reg = stat;
@@ -650,10 +647,11 @@ static void imxmci_dma_irq(int dma, void *devid)
static irqreturn_t imxmci_irq(int irq, void *devid)
{
struct imxmci_host *host = devid;
- uint32_t stat = MMC_STATUS;
+ u32 stat = readw(host->base + MMC_REG_STATUS);
int handled = 1;
- MMC_INT_MASK = host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT;
+ writew(host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT,
+ host->base + MMC_REG_INT_MASK);
atomic_set(&host->stuck_timeout, 0);
host->status_reg = stat;
@@ -671,10 +669,10 @@ static void imxmci_tasklet_fnc(unsigned long data)
unsigned int data_dir_mask = 0; /* STATUS_WR_CRC_ERROR_CODE_MASK */
int timeout = 0;
- if(atomic_read(&host->stuck_timeout) > 4) {
+ if (atomic_read(&host->stuck_timeout) > 4) {
char *what;
timeout = 1;
- stat = MMC_STATUS;
+ stat = readw(host->base + MMC_REG_STATUS);
host->status_reg = stat;
if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
@@ -683,29 +681,37 @@ static void imxmci_tasklet_fnc(unsigned long data)
what = "RESP";
else
if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
- if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events))
+ if (test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events))
what = "DATA";
else
what = "DMA";
else
what = "???";
- dev_err(mmc_dev(host->mmc), "%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n",
- what, stat, MMC_INT_MASK);
- dev_err(mmc_dev(host->mmc), "CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n",
- MMC_CMD_DAT_CONT, MMC_BLK_LEN, MMC_NOB, CCR(host->dma));
+ dev_err(mmc_dev(host->mmc),
+ "%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n",
+ what, stat,
+ readw(host->base + MMC_REG_INT_MASK));
+ dev_err(mmc_dev(host->mmc),
+ "CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n",
+ readw(host->base + MMC_REG_CMD_DAT_CONT),
+ readw(host->base + MMC_REG_BLK_LEN),
+ readw(host->base + MMC_REG_NOB),
+ CCR(host->dma));
dev_err(mmc_dev(host->mmc), "CMD%d, prevCMD%d, bus %d-bit, dma_size = 0x%x\n",
- host->cmd?host->cmd->opcode:0, host->prev_cmd_code, 1<<host->actual_bus_width, host->dma_size);
+ host->cmd ? host->cmd->opcode : 0,
+ host->prev_cmd_code,
+ 1 << host->actual_bus_width, host->dma_size);
}
- if(!host->present || timeout)
+ if (!host->present || timeout)
host->status_reg = STATUS_TIME_OUT_RESP | STATUS_TIME_OUT_READ |
- STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR;
+ STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR;
- if(test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) {
+ if (test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) {
clear_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
- stat = MMC_STATUS;
+ stat = readw(host->base + MMC_REG_STATUS);
/*
* This is not required in theory, but there is chance to miss some flag
* which clears automatically by mask write, FreeScale original code keeps
@@ -713,63 +719,62 @@ static void imxmci_tasklet_fnc(unsigned long data)
*/
stat |= host->status_reg;
- if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
+ if (test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
stat &= ~STATUS_CRC_READ_ERR;
- if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
+ if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
imxmci_busy_wait_for_status(host, &stat,
- STATUS_END_CMD_RESP | STATUS_ERR_MASK,
- 20, "imxmci_tasklet_fnc resp (ERRATUM #4)");
+ STATUS_END_CMD_RESP | STATUS_ERR_MASK,
+ 20, "imxmci_tasklet_fnc resp (ERRATUM #4)");
}
- if(stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) {
- if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
+ if (stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) {
+ if (test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
imxmci_cmd_done(host, stat);
- if(host->data && (stat & STATUS_ERR_MASK))
+ if (host->data && (stat & STATUS_ERR_MASK))
imxmci_data_done(host, stat);
}
- if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) {
- stat |= MMC_STATUS;
- if(imxmci_cpu_driven_data(host, &stat)){
- if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
+ if (test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) {
+ stat |= readw(host->base + MMC_REG_STATUS);
+ if (imxmci_cpu_driven_data(host, &stat)) {
+ if (test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
imxmci_cmd_done(host, stat);
atomic_clear_mask(IMXMCI_PEND_IRQ_m|IMXMCI_PEND_CPU_DATA_m,
- &host->pending_events);
+ &host->pending_events);
imxmci_data_done(host, stat);
}
}
}
- if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) &&
- !test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
+ if (test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) &&
+ !test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
- stat = MMC_STATUS;
+ stat = readw(host->base + MMC_REG_STATUS);
/* Same as above */
stat |= host->status_reg;
- if(host->dma_dir == DMA_TO_DEVICE) {
+ if (host->dma_dir == DMA_TO_DEVICE)
data_dir_mask = STATUS_WRITE_OP_DONE;
- } else {
+ else
data_dir_mask = STATUS_DATA_TRANS_DONE;
- }
- if(stat & data_dir_mask) {
+ if (stat & data_dir_mask) {
clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
imxmci_data_done(host, stat);
}
}
- if(test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) {
+ if (test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) {
- if(host->cmd)
+ if (host->cmd)
imxmci_cmd_done(host, STATUS_TIME_OUT_RESP);
- if(host->data)
+ if (host->data)
imxmci_data_done(host, STATUS_TIME_OUT_READ |
STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR);
- if(host->req)
+ if (host->req)
imxmci_finish_request(host, host->req);
mmc_detect_change(host->mmc, msecs_to_jiffies(100));
@@ -796,9 +801,8 @@ static void imxmci_request(struct mmc_host *mmc, struct mmc_request *req)
if (req->data->flags & MMC_DATA_WRITE)
cmdat |= CMD_DAT_CONT_WRITE;
- if (req->data->flags & MMC_DATA_STREAM) {
+ if (req->data->flags & MMC_DATA_STREAM)
cmdat |= CMD_DAT_CONT_STREAM_BLOCK;
- }
}
imxmci_start_cmd(host, req->cmd, cmdat);
@@ -811,36 +815,37 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
struct imxmci_host *host = mmc_priv(mmc);
int prescaler;
- if( ios->bus_width==MMC_BUS_WIDTH_4 ) {
+ if (ios->bus_width == MMC_BUS_WIDTH_4) {
host->actual_bus_width = MMC_BUS_WIDTH_4;
imx_gpio_mode(PB11_PF_SD_DAT3);
- }else{
+ } else {
host->actual_bus_width = MMC_BUS_WIDTH_1;
imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);
}
- if ( host->power_mode != ios->power_mode ) {
+ if (host->power_mode != ios->power_mode) {
switch (ios->power_mode) {
case MMC_POWER_OFF:
- break;
+ break;
case MMC_POWER_UP:
set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
- break;
+ break;
case MMC_POWER_ON:
- break;
+ break;
}
host->power_mode = ios->power_mode;
}
- if ( ios->clock ) {
+ if (ios->clock) {
unsigned int clk;
+ u16 reg;
/* The prescaler is 5 for PERCLK2 equal to 96MHz
* then 96MHz / 5 = 19.2 MHz
*/
clk = clk_get_rate(host->clk);
- prescaler=(clk+(CLK_RATE*7)/8)/CLK_RATE;
- switch(prescaler) {
+ prescaler = (clk + (CLK_RATE * 7) / 8) / CLK_RATE;
+ switch (prescaler) {
case 0:
case 1: prescaler = 0;
break;
@@ -858,24 +863,29 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
dev_dbg(mmc_dev(host->mmc), "PERCLK2 %d MHz -> prescaler %d\n",
clk, prescaler);
- for(clk=0; clk<8; clk++) {
+ for (clk = 0; clk < 8; clk++) {
int x;
- x = CLK_RATE / (1<<clk);
- if( x <= ios->clock)
+ x = CLK_RATE / (1 << clk);
+ if (x <= ios->clock)
break;
}
- MMC_STR_STP_CLK |= STR_STP_CLK_ENABLE; /* enable controller */
+ /* enable controller */
+ reg = readw(host->base + MMC_REG_STR_STP_CLK);
+ writew(reg | STR_STP_CLK_ENABLE,
+ host->base + MMC_REG_STR_STP_CLK);
imxmci_stop_clock(host);
- MMC_CLK_RATE = (prescaler<<3) | clk;
+ writew((prescaler << 3) | clk, host->base + MMC_REG_CLK_RATE);
/*
* Under my understanding, clock should not be started there, because it would
* initiate SDHC sequencer and send last or random command into card
*/
- /*imxmci_start_clock(host);*/
+ /* imxmci_start_clock(host); */
- dev_dbg(mmc_dev(host->mmc), "MMC_CLK_RATE: 0x%08x\n", MMC_CLK_RATE);
+ dev_dbg(mmc_dev(host->mmc),
+ "MMC_CLK_RATE: 0x%08x\n",
+ readw(host->base + MMC_REG_CLK_RATE));
} else {
imxmci_stop_clock(host);
}
@@ -915,10 +925,10 @@ static void imxmci_check_status(unsigned long data)
tasklet_schedule(&host->tasklet);
}
- if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) ||
- test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
+ if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) ||
+ test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
atomic_inc(&host->stuck_timeout);
- if(atomic_read(&host->stuck_timeout) > 4)
+ if (atomic_read(&host->stuck_timeout) > 4)
tasklet_schedule(&host->tasklet);
} else {
atomic_set(&host->stuck_timeout, 0);
@@ -934,6 +944,7 @@ static int imxmci_probe(struct platform_device *pdev)
struct imxmci_host *host = NULL;
struct resource *r;
int ret = 0, irq;
+ u16 rev_no;
printk(KERN_INFO "i.MX mmc driver\n");
@@ -942,7 +953,8 @@ static int imxmci_probe(struct platform_device *pdev)
if (!r || irq < 0)
return -ENXIO;
- if (!request_mem_region(r->start, 0x100, pdev->name))
+ r = request_mem_region(r->start, resource_size(r), pdev->name);
+ if (!r)
return -EBUSY;
mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev);
@@ -966,6 +978,12 @@ static int imxmci_probe(struct platform_device *pdev)
mmc->max_blk_count = 65535;
host = mmc_priv(mmc);
+ host->base = ioremap(r->start, resource_size(r));
+ if (!host->base) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
host->mmc = mmc;
host->dma_allocated = 0;
host->pdata = pdev->dev.platform_data;
@@ -993,18 +1011,20 @@ static int imxmci_probe(struct platform_device *pdev)
imx_gpio_mode(PB12_PF_SD_CLK);
imx_gpio_mode(PB13_PF_SD_CMD);
- imxmci_softreset();
+ imxmci_softreset(host);
- if ( MMC_REV_NO != 0x390 ) {
+ rev_no = readw(host->base + MMC_REG_REV_NO);
+ if (rev_no != 0x390) {
dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n",
- MMC_REV_NO);
+ readw(host->base + MMC_REG_REV_NO));
goto out;
}
- MMC_READ_TO = 0x2db4; /* recommended in data sheet */
+ /* recommended in data sheet */
+ writew(0x2db4, host->base + MMC_REG_READ_TO);
host->imask = IMXMCI_INT_MASK_DEFAULT;
- MMC_INT_MASK = host->imask;
+ writew(host->imask, host->base + MMC_REG_INT_MASK);
host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW);
if(host->dma < 0) {
@@ -1012,7 +1032,7 @@ static int imxmci_probe(struct platform_device *pdev)
ret = -EBUSY;
goto out;
}
- host->dma_allocated=1;
+ host->dma_allocated = 1;
imx_dma_setup_handlers(host->dma, imxmci_dma_irq, NULL, host);
tasklet_init(&host->tasklet, imxmci_tasklet_fnc, (unsigned long)host);
@@ -1032,7 +1052,7 @@ static int imxmci_probe(struct platform_device *pdev)
host->timer.data = (unsigned long)host;
host->timer.function = imxmci_check_status;
add_timer(&host->timer);
- mod_timer(&host->timer, jiffies + (HZ>>1));
+ mod_timer(&host->timer, jiffies + (HZ >> 1));
platform_set_drvdata(pdev, mmc);
@@ -1042,18 +1062,20 @@ static int imxmci_probe(struct platform_device *pdev)
out:
if (host) {
- if(host->dma_allocated){
+ if (host->dma_allocated) {
imx_dma_free(host->dma);
- host->dma_allocated=0;
+ host->dma_allocated = 0;
}
if (host->clk) {
clk_disable(host->clk);
clk_put(host->clk);
}
+ if (host->base)
+ iounmap(host->base);
}
if (mmc)
mmc_free_host(mmc);
- release_mem_region(r->start, 0x100);
+ release_mem_region(r->start, resource_size(r));
return ret;
}
@@ -1072,9 +1094,10 @@ static int imxmci_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
free_irq(host->irq, host);
- if(host->dma_allocated){
+ iounmap(host->base);
+ if (host->dma_allocated) {
imx_dma_free(host->dma);
- host->dma_allocated=0;
+ host->dma_allocated = 0;
}
tasklet_kill(&host->tasklet);
@@ -1082,7 +1105,7 @@ static int imxmci_remove(struct platform_device *pdev)
clk_disable(host->clk);
clk_put(host->clk);
- release_mem_region(host->res->start, 0x100);
+ release_mem_region(host->res->start, resource_size(host->res));
mmc_free_host(mmc);
}
@@ -1109,7 +1132,7 @@ static int imxmci_resume(struct platform_device *dev)
if (mmc) {
host = mmc_priv(mmc);
- if(host)
+ if (host)
set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
ret = mmc_resume_host(mmc);
}
diff --git a/drivers/mmc/host/imxmmc.h b/drivers/mmc/host/imxmmc.h
index e5339e334dbb..09d5d4ee3a77 100644
--- a/drivers/mmc/host/imxmmc.h
+++ b/drivers/mmc/host/imxmmc.h
@@ -1,24 +1,21 @@
+#define MMC_REG_STR_STP_CLK 0x00
+#define MMC_REG_STATUS 0x04
+#define MMC_REG_CLK_RATE 0x08
+#define MMC_REG_CMD_DAT_CONT 0x0C
+#define MMC_REG_RES_TO 0x10
+#define MMC_REG_READ_TO 0x14
+#define MMC_REG_BLK_LEN 0x18
+#define MMC_REG_NOB 0x1C
+#define MMC_REG_REV_NO 0x20
+#define MMC_REG_INT_MASK 0x24
+#define MMC_REG_CMD 0x28
+#define MMC_REG_ARGH 0x2C
+#define MMC_REG_ARGL 0x30
+#define MMC_REG_RES_FIFO 0x34
+#define MMC_REG_BUFFER_ACCESS 0x38
-# define __REG16(x) (*((volatile u16 *)IO_ADDRESS(x)))
-
-#define MMC_STR_STP_CLK __REG16(IMX_MMC_BASE + 0x00)
-#define MMC_STATUS __REG16(IMX_MMC_BASE + 0x04)
-#define MMC_CLK_RATE __REG16(IMX_MMC_BASE + 0x08)
-#define MMC_CMD_DAT_CONT __REG16(IMX_MMC_BASE + 0x0C)
-#define MMC_RES_TO __REG16(IMX_MMC_BASE + 0x10)
-#define MMC_READ_TO __REG16(IMX_MMC_BASE + 0x14)
-#define MMC_BLK_LEN __REG16(IMX_MMC_BASE + 0x18)
-#define MMC_NOB __REG16(IMX_MMC_BASE + 0x1C)
-#define MMC_REV_NO __REG16(IMX_MMC_BASE + 0x20)
-#define MMC_INT_MASK __REG16(IMX_MMC_BASE + 0x24)
-#define MMC_CMD __REG16(IMX_MMC_BASE + 0x28)
-#define MMC_ARGH __REG16(IMX_MMC_BASE + 0x2C)
-#define MMC_ARGL __REG16(IMX_MMC_BASE + 0x30)
-#define MMC_RES_FIFO __REG16(IMX_MMC_BASE + 0x34)
-#define MMC_BUFFER_ACCESS __REG16(IMX_MMC_BASE + 0x38)
-#define MMC_BUFFER_ACCESS_OFS 0x38
-
-
+#define STR_STP_CLK_IPG_CLK_GATE_DIS (1<<15)
+#define STR_STP_CLK_IPG_PERCLK_GATE_DIS (1<<14)
#define STR_STP_CLK_ENDIAN (1<<5)
#define STR_STP_CLK_RESET (1<<3)
#define STR_STP_CLK_ENABLE (1<<2)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 2fadf323c696..1bcbdd6763ac 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -500,7 +500,7 @@ static int mmci_probe(struct amba_device *dev, void *id)
}
host = mmc_priv(mmc);
- host->clk = clk_get(&dev->dev, "MCLK");
+ host->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk);
host->clk = NULL;
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 1b9fc3c6b875..67d7b7fef084 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -1015,7 +1015,7 @@ static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data
}
if (is_read) {
- if (host->id == 1) {
+ if (host->id == 0) {
sync_dev = OMAP_DMA_MMC_RX;
dma_dev_name = "MMC1 read";
} else {
@@ -1023,7 +1023,7 @@ static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data
dma_dev_name = "MMC2 read";
}
} else {
- if (host->id == 1) {
+ if (host->id == 0) {
sync_dev = OMAP_DMA_MMC_TX;
dma_dev_name = "MMC1 write";
} else {
@@ -1317,7 +1317,7 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)
host->slots[id] = slot;
mmc->caps = 0;
- if (host->pdata->conf.wire4)
+ if (host->pdata->slots[id].wires >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;
mmc->ops = &mmc_omap_ops;
@@ -1451,6 +1451,7 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
host->irq = irq;
host->use_dma = 1;
+ host->dev->dma_mask = &pdata->dma_mask;
host->dma_ch = -1;
host->irq = irq;
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index ebfaa9960939..f88cc7406354 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -26,11 +26,12 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mmc/host.h>
+#include <linux/io.h>
-#include <asm/dma.h>
-#include <asm/io.h>
#include <asm/sizes.h>
+#include <mach/dma.h>
+#include <mach/hardware.h>
#include <mach/pxa-regs.h>
#include <mach/mmc.h>
@@ -533,7 +534,7 @@ static int pxamci_probe(struct platform_device *pdev)
host->pdata = pdev->dev.platform_data;
host->clkrt = CLKRT_OFF;
- host->clk = clk_get(&pdev->dev, "MMCCLK");
+ host->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk);
host->clk = NULL;
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 3b2085b57769..fcc98a4cce3c 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -25,7 +25,7 @@
#include <mach/regs-sdi.h>
#include <mach/regs-gpio.h>
-#include <asm/plat-s3c24xx/mci.h>
+#include <plat/mci.h>
#include "s3cmci.h"
diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c
index 3aa018c092f8..42969fe051b2 100644
--- a/drivers/mtd/maps/dc21285.c
+++ b/drivers/mtd/maps/dc21285.c
@@ -32,16 +32,15 @@ static struct mtd_info *dc21285_mtd;
*/
static void nw_en_write(void)
{
- extern spinlock_t gpio_lock;
unsigned long flags;
/*
* we want to write a bit pattern XXX1 to Xilinx to enable
* the write gate, which will be open for about the next 2ms.
*/
- spin_lock_irqsave(&gpio_lock, flags);
- cpld_modify(1, 1);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
/*
* let the ISA bus to catch on...
diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c
index dcdb1f17577d..3ea1de9be720 100644
--- a/drivers/mtd/maps/ixp2000.c
+++ b/drivers/mtd/maps/ixp2000.c
@@ -170,7 +170,7 @@ static int ixp2000_flash_probe(struct platform_device *dev)
err = -ENOMEM;
goto Error;
}
- memzero(info, sizeof(struct ixp2000_flash_info));
+ memset(info, 0, sizeof(struct ixp2000_flash_info));
platform_set_drvdata(dev, info);
diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c
index 9c7a5fbd4e51..16555cbeaea4 100644
--- a/drivers/mtd/maps/ixp4xx.c
+++ b/drivers/mtd/maps/ixp4xx.c
@@ -201,7 +201,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
err = -ENOMEM;
goto Error;
}
- memzero(info, sizeof(struct ixp4xx_flash_info));
+ memset(info, 0, sizeof(struct ixp4xx_flash_info));
platform_set_drvdata(dev, info);
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 1c2e9450d663..f8ae0400c49c 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -408,7 +408,7 @@ config MTD_NAND_FSL_UPM
config MTD_NAND_MXC
tristate "MXC NAND support"
- depends on ARCH_MX2
+ depends on ARCH_MX2 || ARCH_MX3
help
This enables the driver for the NAND flash controller on the
MXC processors.
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 15f0a26730ae..fc4144495610 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -20,8 +20,8 @@
#include <linux/mtd/partitions.h>
#include <linux/io.h>
#include <linux/irq.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/pxa-regs.h>
#include <mach/pxa3xx_nand.h>
@@ -1080,7 +1080,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
this = &info->nand_chip;
mtd->priv = info;
- info->clk = clk_get(&pdev->dev, "NANDCLK");
+ info->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed to get nand clock\n");
ret = PTR_ERR(info->clk);
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 556139ed1fdf..8e375d5fe231 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -45,8 +45,8 @@
#include <asm/io.h>
-#include <asm/plat-s3c/regs-nand.h>
-#include <asm/plat-s3c/nand.h>
+#include <plat/regs-nand.h>
+#include <plat/nand.h>
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
static int hardware_ecc = 1;
@@ -818,7 +818,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
goto exit_error;
}
- memzero(info, sizeof(*info));
+ memset(info, 0, sizeof(*info));
platform_set_drvdata(pdev, info);
spin_lock_init(&info->controller.lock);
@@ -883,7 +883,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
goto exit_error;
}
- memzero(info->mtds, size);
+ memset(info->mtds, 0, size);
/* initialise all possible chips */
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index a7e4d985f5ef..d1e0b8e7224b 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -149,7 +149,7 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)
INIT_COMPLETION(c->irq_done);
if (c->gpio_irq) {
- result = omap_get_gpio_datain(c->gpio_irq);
+ result = gpio_get_value(c->gpio_irq);
if (result == -1) {
ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
intr = read_reg(c, ONENAND_REG_INTERRUPT);
@@ -634,9 +634,9 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
"OneNAND\n", c->gpio_irq);
goto err_iounmap;
}
- omap_set_gpio_direction(c->gpio_irq, 1);
+ gpio_direction_input(c->gpio_irq);
- if ((r = request_irq(OMAP_GPIO_IRQ(c->gpio_irq),
+ if ((r = request_irq(gpio_to_irq(c->gpio_irq),
omap2_onenand_interrupt, IRQF_TRIGGER_RISING,
pdev->dev.driver->name, c)) < 0)
goto err_release_gpio;
@@ -723,7 +723,7 @@ err_release_dma:
if (c->dma_channel != -1)
omap_free_dma(c->dma_channel);
if (c->gpio_irq)
- free_irq(OMAP_GPIO_IRQ(c->gpio_irq), c);
+ free_irq(gpio_to_irq(c->gpio_irq), c);
err_release_gpio:
if (c->gpio_irq)
omap_free_gpio(c->gpio_irq);
@@ -760,7 +760,7 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev)
omap2_onenand_shutdown(pdev);
platform_set_drvdata(pdev, NULL);
if (c->gpio_irq) {
- free_irq(OMAP_GPIO_IRQ(c->gpio_irq), c);
+ free_irq(gpio_to_irq(c->gpio_irq), c);
omap_free_gpio(c->gpio_irq);
}
iounmap(c->onenand.base);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 72a9212da865..9a18270c1081 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2399,9 +2399,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_T3_DEPENDS
+ tristate
+ depends on PCI && INET
+ default y
+
config CHELSIO_T3
tristate "Chelsio Communications T3 10Gb Ethernet support"
- depends on PCI && INET
+ depends on CHELSIO_T3_DEPENDS
select FW_LOADER
select INET_LRO
help
diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c
index 6ecc600c1bcc..3ec20cc18b0c 100644
--- a/drivers/net/arm/ep93xx_eth.c
+++ b/drivers/net/arm/ep93xx_eth.c
@@ -307,7 +307,7 @@ poll_some_more:
}
spin_unlock_irq(&ep->rx_lock);
- if (more && netif_rx_reschedule(dev, napi))
+ if (more && netif_rx_reschedule(napi))
goto poll_some_more;
}
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c
index 26af411fc428..5fce1d5c1a1a 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/arm/ixp4xx_eth.c
@@ -504,7 +504,7 @@ static int eth_poll(struct napi_struct *napi, int budget)
netif_rx_complete(napi);
qmgr_enable_irq(rxq);
if (!qmgr_stat_empty(rxq) &&
- netif_rx_reschedule(dev, napi)) {
+ netif_rx_reschedule(napi)) {
#if DEBUG_RX
printk(KERN_DEBUG "%s: eth_poll"
" netif_rx_reschedule successed\n",
diff --git a/drivers/net/arm/ks8695net.c b/drivers/net/arm/ks8695net.c
index 592daee9dc28..9ad22d1b00fd 100644
--- a/drivers/net/arm/ks8695net.c
+++ b/drivers/net/arm/ks8695net.c
@@ -29,7 +29,6 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
-#include <linux/delay.h>
#include <linux/io.h>
#include <asm/irq.h>
diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c
index d548a45d59d5..ff6497658a45 100644
--- a/drivers/net/cs89x0.c
+++ b/drivers/net/cs89x0.c
@@ -170,11 +170,7 @@ static char version[] __initdata =
/* The cs8900 has 4 IRQ pins, software selectable. cs8900_irq_map maps
them to system IRQ numbers. This mapping is card specific and is set to
the configuration of the Cirrus Eval board for this chip. */
-#ifdef CONFIG_ARCH_CLPS7500
-static unsigned int netcard_portlist[] __used __initdata =
- { 0x80090303, 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
-static unsigned int cs8900_irq_map[] = {12,0,0,0};
-#elif defined(CONFIG_SH_HICOSH4)
+#if defined(CONFIG_SH_HICOSH4)
static unsigned int netcard_portlist[] __used __initdata =
{ 0x0300, 0};
static unsigned int cs8900_irq_map[] = {1,0,0,0};
diff --git a/drivers/net/eexpress.h b/drivers/net/eexpress.h
index 707df3fcfe40..dc9c6ea289e9 100644
--- a/drivers/net/eexpress.h
+++ b/drivers/net/eexpress.h
@@ -68,17 +68,17 @@
*/
/* these functions take the SCB status word and test the relevant status bit */
-#define SCB_complete(s) ((s&0x8000)!=0)
-#define SCB_rxdframe(s) ((s&0x4000)!=0)
-#define SCB_CUdead(s) ((s&0x2000)!=0)
-#define SCB_RUdead(s) ((s&0x1000)!=0)
-#define SCB_ack(s) (s & 0xf000)
+#define SCB_complete(s) (((s) & 0x8000) != 0)
+#define SCB_rxdframe(s) (((s) & 0x4000) != 0)
+#define SCB_CUdead(s) (((s) & 0x2000) != 0)
+#define SCB_RUdead(s) (((s) & 0x1000) != 0)
+#define SCB_ack(s) ((s) & 0xf000)
/* Command unit status: 0=idle, 1=suspended, 2=active */
-#define SCB_CUstat(s) ((s&0x0300)>>8)
+#define SCB_CUstat(s) (((s)&0x0300)>>8)
/* Receive unit status: 0=idle, 1=suspended, 2=out of resources, 4=ready */
-#define SCB_RUstat(s) ((s&0x0070)>>4)
+#define SCB_RUstat(s) (((s)&0x0070)>>4)
/* SCB commands */
#define SCB_CUnop 0x0000
@@ -98,18 +98,18 @@
* Command block defines
*/
-#define Stat_Done(s) ((s&0x8000)!=0)
-#define Stat_Busy(s) ((s&0x4000)!=0)
-#define Stat_OK(s) ((s&0x2000)!=0)
-#define Stat_Abort(s) ((s&0x1000)!=0)
-#define Stat_STFail ((s&0x0800)!=0)
-#define Stat_TNoCar(s) ((s&0x0400)!=0)
-#define Stat_TNoCTS(s) ((s&0x0200)!=0)
-#define Stat_TNoDMA(s) ((s&0x0100)!=0)
-#define Stat_TDefer(s) ((s&0x0080)!=0)
-#define Stat_TColl(s) ((s&0x0040)!=0)
-#define Stat_TXColl(s) ((s&0x0020)!=0)
-#define Stat_NoColl(s) (s&0x000f)
+#define Stat_Done(s) (((s) & 0x8000) != 0)
+#define Stat_Busy(s) (((s) & 0x4000) != 0)
+#define Stat_OK(s) (((s) & 0x2000) != 0)
+#define Stat_Abort(s) (((s) & 0x1000) != 0)
+#define Stat_STFail (((s) & 0x0800) != 0)
+#define Stat_TNoCar(s) (((s) & 0x0400) != 0)
+#define Stat_TNoCTS(s) (((s) & 0x0200) != 0)
+#define Stat_TNoDMA(s) (((s) & 0x0100) != 0)
+#define Stat_TDefer(s) (((s) & 0x0080) != 0)
+#define Stat_TColl(s) (((s) & 0x0040) != 0)
+#define Stat_TXColl(s) (((s) & 0x0020) != 0)
+#define Stat_NoColl(s) ((s) & 0x000f)
/* Cmd_END will end AFTER the command if this is the first
* command block after an SCB_CUstart, but BEFORE the command
@@ -136,16 +136,16 @@
* Frame Descriptor (Receive block) defines
*/
-#define FD_Done(s) ((s&0x8000)!=0)
-#define FD_Busy(s) ((s&0x4000)!=0)
-#define FD_OK(s) ((s&0x2000)!=0)
+#define FD_Done(s) (((s) & 0x8000) != 0)
+#define FD_Busy(s) (((s) & 0x4000) != 0)
+#define FD_OK(s) (((s) & 0x2000) != 0)
-#define FD_CRC(s) ((s&0x0800)!=0)
-#define FD_Align(s) ((s&0x0400)!=0)
-#define FD_Resrc(s) ((s&0x0200)!=0)
-#define FD_DMA(s) ((s&0x0100)!=0)
-#define FD_Short(s) ((s&0x0080)!=0)
-#define FD_NoEOF(s) ((s&0x0040)!=0)
+#define FD_CRC(s) (((s) & 0x0800) != 0)
+#define FD_Align(s) (((s) & 0x0400) != 0)
+#define FD_Resrc(s) (((s) & 0x0200) != 0)
+#define FD_DMA(s) (((s) & 0x0100) != 0)
+#define FD_Short(s) (((s) & 0x0080) != 0)
+#define FD_NoEOF(s) (((s) & 0x0040) != 0)
struct rfd_header {
volatile unsigned long flags;
diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c
index a0ee05318155..004a9aab3a50 100644
--- a/drivers/net/irda/pxaficp_ir.c
+++ b/drivers/net/irda/pxaficp_ir.c
@@ -22,9 +22,53 @@
#include <net/irda/wrapper.h>
#include <net/irda/irda_device.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/irda.h>
+#include <mach/hardware.h>
#include <mach/pxa-regs.h>
+#include <mach/regs-uart.h>
+
+#define FICP __REG(0x40800000) /* Start of FICP area */
+#define ICCR0 __REG(0x40800000) /* ICP Control Register 0 */
+#define ICCR1 __REG(0x40800004) /* ICP Control Register 1 */
+#define ICCR2 __REG(0x40800008) /* ICP Control Register 2 */
+#define ICDR __REG(0x4080000c) /* ICP Data Register */
+#define ICSR0 __REG(0x40800014) /* ICP Status Register 0 */
+#define ICSR1 __REG(0x40800018) /* ICP Status Register 1 */
+
+#define ICCR0_AME (1 << 7) /* Address match enable */
+#define ICCR0_TIE (1 << 6) /* Transmit FIFO interrupt enable */
+#define ICCR0_RIE (1 << 5) /* Recieve FIFO interrupt enable */
+#define ICCR0_RXE (1 << 4) /* Receive enable */
+#define ICCR0_TXE (1 << 3) /* Transmit enable */
+#define ICCR0_TUS (1 << 2) /* Transmit FIFO underrun select */
+#define ICCR0_LBM (1 << 1) /* Loopback mode */
+#define ICCR0_ITR (1 << 0) /* IrDA transmission */
+
+#define ICCR2_RXP (1 << 3) /* Receive Pin Polarity select */
+#define ICCR2_TXP (1 << 2) /* Transmit Pin Polarity select */
+#define ICCR2_TRIG (3 << 0) /* Receive FIFO Trigger threshold */
+#define ICCR2_TRIG_8 (0 << 0) /* >= 8 bytes */
+#define ICCR2_TRIG_16 (1 << 0) /* >= 16 bytes */
+#define ICCR2_TRIG_32 (2 << 0) /* >= 32 bytes */
+
+#ifdef CONFIG_PXA27x
+#define ICSR0_EOC (1 << 6) /* DMA End of Descriptor Chain */
+#endif
+#define ICSR0_FRE (1 << 5) /* Framing error */
+#define ICSR0_RFS (1 << 4) /* Receive FIFO service request */
+#define ICSR0_TFS (1 << 3) /* Transnit FIFO service request */
+#define ICSR0_RAB (1 << 2) /* Receiver abort */
+#define ICSR0_TUR (1 << 1) /* Trunsmit FIFO underun */
+#define ICSR0_EIF (1 << 0) /* End/Error in FIFO */
+
+#define ICSR1_ROR (1 << 6) /* Receiver FIFO underrun */
+#define ICSR1_CRE (1 << 5) /* CRC error */
+#define ICSR1_EOF (1 << 4) /* End of frame */
+#define ICSR1_TNF (1 << 3) /* Transmit FIFO not full */
+#define ICSR1_RNE (1 << 2) /* Receive FIFO not empty */
+#define ICSR1_TBY (1 << 1) /* Tramsmiter busy flag */
+#define ICSR1_RSY (1 << 0) /* Recevier synchronized flag */
#define IrSR_RXPL_NEG_IS_ZERO (1<<4)
#define IrSR_RXPL_POS_IS_ZERO 0x0
diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c
index ccde5829ba21..d302bcf4c148 100644
--- a/drivers/net/irda/sa1100_ir.c
+++ b/drivers/net/irda/sa1100_ir.c
@@ -36,7 +36,7 @@
#include <net/irda/irda_device.h>
#include <asm/irq.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/hardware.h>
#include <asm/mach/irda.h>
diff --git a/drivers/net/mlx4/en_main.c b/drivers/net/mlx4/en_main.c
index c1c05852a95e..eda72dd2120f 100644
--- a/drivers/net/mlx4/en_main.c
+++ b/drivers/net/mlx4/en_main.c
@@ -169,13 +169,10 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) {
mlx4_info(mdev, "Using %d tx rings for port:%d\n",
mdev->profile.prof[i].tx_ring_num, i);
- if (!mdev->profile.prof[i].rx_ring_num) {
- mdev->profile.prof[i].rx_ring_num = dev->caps.num_comp_vectors;
- mlx4_info(mdev, "Defaulting to %d rx rings for port:%d\n",
- mdev->profile.prof[i].rx_ring_num, i);
- } else
- mlx4_info(mdev, "Using %d rx rings for port:%d\n",
- mdev->profile.prof[i].rx_ring_num, i);
+ mdev->profile.prof[i].rx_ring_num =
+ min_t(int, dev->caps.num_comp_vectors, MAX_RX_RINGS);
+ mlx4_info(mdev, "Defaulting to %d rx rings for port:%d\n",
+ mdev->profile.prof[i].rx_ring_num, i);
}
/* Create our own workqueue for reset/multicast tasks
diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c
index ebada3c7aff2..15bb38d99304 100644
--- a/drivers/net/mlx4/en_netdev.c
+++ b/drivers/net/mlx4/en_netdev.c
@@ -552,7 +552,7 @@ static void mlx4_en_linkstate(struct work_struct *work)
}
-static int mlx4_en_start_port(struct net_device *dev)
+int mlx4_en_start_port(struct net_device *dev)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
@@ -707,7 +707,7 @@ cq_err:
}
-static void mlx4_en_stop_port(struct net_device *dev)
+void mlx4_en_stop_port(struct net_device *dev)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
@@ -826,7 +826,7 @@ static int mlx4_en_close(struct net_device *dev)
return 0;
}
-static void mlx4_en_free_resources(struct mlx4_en_priv *priv)
+void mlx4_en_free_resources(struct mlx4_en_priv *priv)
{
int i;
@@ -845,7 +845,7 @@ static void mlx4_en_free_resources(struct mlx4_en_priv *priv)
}
}
-static int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
+int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
{
struct mlx4_en_dev *mdev = priv->mdev;
struct mlx4_en_port_profile *prof = priv->prof;
diff --git a/drivers/net/mlx4/en_params.c b/drivers/net/mlx4/en_params.c
index 047b37f5a747..cfeef0f1bacc 100644
--- a/drivers/net/mlx4/en_params.c
+++ b/drivers/net/mlx4/en_params.c
@@ -65,15 +65,6 @@ MLX4_EN_PARM_INT(pfctx, 0, "Priority based Flow Control policy on TX[7:0]."
MLX4_EN_PARM_INT(pfcrx, 0, "Priority based Flow Control policy on RX[7:0]."
" Per priority bit mask");
-MLX4_EN_PARM_INT(rx_ring_num1, 0, "Number or Rx rings for port 1 (0 = #cores)");
-MLX4_EN_PARM_INT(rx_ring_num2, 0, "Number or Rx rings for port 2 (0 = #cores)");
-
-MLX4_EN_PARM_INT(tx_ring_size1, MLX4_EN_AUTO_CONF, "Tx ring size for port 1");
-MLX4_EN_PARM_INT(tx_ring_size2, MLX4_EN_AUTO_CONF, "Tx ring size for port 2");
-MLX4_EN_PARM_INT(rx_ring_size1, MLX4_EN_AUTO_CONF, "Rx ring size for port 1");
-MLX4_EN_PARM_INT(rx_ring_size2, MLX4_EN_AUTO_CONF, "Rx ring size for port 2");
-
-
int mlx4_en_get_profile(struct mlx4_en_dev *mdev)
{
struct mlx4_en_profile *params = &mdev->profile;
@@ -87,6 +78,8 @@ int mlx4_en_get_profile(struct mlx4_en_dev *mdev)
params->prof[i].rx_ppp = pfcrx;
params->prof[i].tx_pause = 1;
params->prof[i].tx_ppp = pfctx;
+ params->prof[i].tx_ring_size = MLX4_EN_DEF_TX_RING_SIZE;
+ params->prof[i].rx_ring_size = MLX4_EN_DEF_RX_RING_SIZE;
}
if (pfcrx || pfctx) {
params->prof[1].tx_ring_num = MLX4_EN_TX_RING_NUM;
@@ -95,32 +88,7 @@ int mlx4_en_get_profile(struct mlx4_en_dev *mdev)
params->prof[1].tx_ring_num = 1;
params->prof[2].tx_ring_num = 1;
}
- params->prof[1].rx_ring_num = min_t(int, rx_ring_num1, MAX_RX_RINGS);
- params->prof[2].rx_ring_num = min_t(int, rx_ring_num2, MAX_RX_RINGS);
-
- if (tx_ring_size1 == MLX4_EN_AUTO_CONF)
- tx_ring_size1 = MLX4_EN_DEF_TX_RING_SIZE;
- params->prof[1].tx_ring_size =
- (tx_ring_size1 < MLX4_EN_MIN_TX_SIZE) ?
- MLX4_EN_MIN_TX_SIZE : roundup_pow_of_two(tx_ring_size1);
-
- if (tx_ring_size2 == MLX4_EN_AUTO_CONF)
- tx_ring_size2 = MLX4_EN_DEF_TX_RING_SIZE;
- params->prof[2].tx_ring_size =
- (tx_ring_size2 < MLX4_EN_MIN_TX_SIZE) ?
- MLX4_EN_MIN_TX_SIZE : roundup_pow_of_two(tx_ring_size2);
-
- if (rx_ring_size1 == MLX4_EN_AUTO_CONF)
- rx_ring_size1 = MLX4_EN_DEF_RX_RING_SIZE;
- params->prof[1].rx_ring_size =
- (rx_ring_size1 < MLX4_EN_MIN_RX_SIZE) ?
- MLX4_EN_MIN_RX_SIZE : roundup_pow_of_two(rx_ring_size1);
-
- if (rx_ring_size2 == MLX4_EN_AUTO_CONF)
- rx_ring_size2 = MLX4_EN_DEF_RX_RING_SIZE;
- params->prof[2].rx_ring_size =
- (rx_ring_size2 < MLX4_EN_MIN_RX_SIZE) ?
- MLX4_EN_MIN_RX_SIZE : roundup_pow_of_two(rx_ring_size2);
+
return 0;
}
@@ -417,6 +385,54 @@ static void mlx4_en_get_pauseparam(struct net_device *dev,
pause->rx_pause = priv->prof->rx_pause;
}
+static int mlx4_en_set_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *param)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+ struct mlx4_en_dev *mdev = priv->mdev;
+ u32 rx_size, tx_size;
+ int port_up = 0;
+ int err = 0;
+
+ if (param->rx_jumbo_pending || param->rx_mini_pending)
+ return -EINVAL;
+
+ rx_size = roundup_pow_of_two(param->rx_pending);
+ rx_size = max_t(u32, rx_size, MLX4_EN_MIN_RX_SIZE);
+ tx_size = roundup_pow_of_two(param->tx_pending);
+ tx_size = max_t(u32, tx_size, MLX4_EN_MIN_TX_SIZE);
+
+ if (rx_size == priv->prof->rx_ring_size &&
+ tx_size == priv->prof->tx_ring_size)
+ return 0;
+
+ mutex_lock(&mdev->state_lock);
+ if (priv->port_up) {
+ port_up = 1;
+ mlx4_en_stop_port(dev);
+ }
+
+ mlx4_en_free_resources(priv);
+
+ priv->prof->tx_ring_size = tx_size;
+ priv->prof->rx_ring_size = rx_size;
+
+ err = mlx4_en_alloc_resources(priv);
+ if (err) {
+ mlx4_err(mdev, "Failed reallocating port resources\n");
+ goto out;
+ }
+ if (port_up) {
+ err = mlx4_en_start_port(dev);
+ if (err)
+ mlx4_err(mdev, "Failed starting port\n");
+ }
+
+out:
+ mutex_unlock(&mdev->state_lock);
+ return err;
+}
+
static void mlx4_en_get_ringparam(struct net_device *dev,
struct ethtool_ringparam *param)
{
@@ -456,6 +472,7 @@ const struct ethtool_ops mlx4_en_ethtool_ops = {
.get_pauseparam = mlx4_en_get_pauseparam,
.set_pauseparam = mlx4_en_set_pauseparam,
.get_ringparam = mlx4_en_get_ringparam,
+ .set_ringparam = mlx4_en_set_ringparam,
.get_flags = ethtool_op_get_flags,
.set_flags = ethtool_op_set_flags,
};
diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h
index e78209768def..2e96c7b2180a 100644
--- a/drivers/net/mlx4/mlx4_en.h
+++ b/drivers/net/mlx4/mlx4_en.h
@@ -489,6 +489,12 @@ void mlx4_en_destroy_netdev(struct net_device *dev);
int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
struct mlx4_en_port_profile *prof);
+int mlx4_en_start_port(struct net_device *dev);
+void mlx4_en_stop_port(struct net_device *dev);
+
+void mlx4_en_free_resources(struct mlx4_en_priv *priv);
+int mlx4_en_alloc_resources(struct mlx4_en_priv *priv);
+
int mlx4_en_get_profile(struct mlx4_en_dev *mdev);
int mlx4_en_create_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c
index dcd199045613..5b7a574ce571 100644
--- a/drivers/net/pasemi_mac.c
+++ b/drivers/net/pasemi_mac.c
@@ -954,7 +954,6 @@ static irqreturn_t pasemi_mac_rx_intr(int irq, void *data)
{
const struct pasemi_mac_rxring *rxring = data;
struct pasemi_mac *mac = rxring->mac;
- struct net_device *dev = mac->netdev;
const struct pasemi_dmachan *chan = &rxring->chan;
unsigned int reg;
@@ -1634,7 +1633,6 @@ static void pasemi_mac_set_rx_mode(struct net_device *dev)
static int pasemi_mac_poll(struct napi_struct *napi, int budget)
{
struct pasemi_mac *mac = container_of(napi, struct pasemi_mac, napi);
- struct net_device *dev = mac->netdev;
int pkts;
pasemi_mac_clean_tx(tx_ring(mac));
diff --git a/drivers/net/smc911x.h b/drivers/net/smc911x.h
index cc7d85bdfb3e..870b4c33f108 100644
--- a/drivers/net/smc911x.h
+++ b/drivers/net/smc911x.h
@@ -200,6 +200,9 @@ static inline void SMC_outsl(struct smc911x_local *lp, int reg,
#ifdef SMC_USE_PXA_DMA
+
+#include <mach/dma.h>
+
/*
* Define the request and free functions
* These are unfortunately architecture specific as no generic allocation
diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h
index 3e7c6a3cbc65..c4ccd121bc9c 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/smc91x.h
@@ -493,7 +493,8 @@ struct smc_local {
* as RX which can overrun memory and lose packets.
*/
#include <linux/dma-mapping.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
+#include <mach/hardware.h>
#include <mach/pxa-regs.h>
#ifdef SMC_insl
diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c
index 5e989d884ddd..dc3f1108884d 100644
--- a/drivers/net/smsc911x.c
+++ b/drivers/net/smsc911x.c
@@ -1484,13 +1484,13 @@ static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id)
}
if (likely(intsts & inten & INT_STS_RSFL_)) {
- if (likely(netif_rx_schedule_prep(dev, &pdata->napi))) {
+ if (likely(netif_rx_schedule_prep(&pdata->napi))) {
/* Disable Rx interrupts */
temp = smsc911x_reg_read(pdata, INT_EN);
temp &= (~INT_EN_RSFL_EN_);
smsc911x_reg_write(pdata, INT_EN, temp);
/* Schedule a NAPI poll */
- __netif_rx_schedule(dev, &pdata->napi);
+ __netif_rx_schedule(&pdata->napi);
} else {
SMSC_WARNING(RX_ERR,
"netif_rx_schedule_prep failed");
diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c
index c5c123d3af57..88d2c67788df 100644
--- a/drivers/net/spider_net.c
+++ b/drivers/net/spider_net.c
@@ -1277,7 +1277,6 @@ bad_desc:
static int spider_net_poll(struct napi_struct *napi, int budget)
{
struct spider_net_card *card = container_of(napi, struct spider_net_card, napi);
- struct net_device *netdev = card->netdev;
int packets_done = 0;
while (packets_done < budget) {
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 666c1d98cdaf..69f9a0ec764d 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -343,7 +343,7 @@ static void tun_net_init(struct net_device *dev)
break;
case TUN_TAP_DEV:
- dev->netdev_ops = &tun_netdev_ops;
+ dev->netdev_ops = &tap_netdev_ops;
/* Ethernet TAP Device */
ether_setup(dev);
diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
index 198ce3cf378a..9f7896a25f1b 100644
--- a/drivers/net/usb/hso.c
+++ b/drivers/net/usb/hso.c
@@ -2831,7 +2831,7 @@ static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf,
for (i = 0; i < iface->desc.bNumEndpoints; i++) {
endp = &iface->endpoint[i].desc;
if (((endp->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir) &&
- ((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == type))
+ (usb_endpoint_type(endp) == type))
return endp;
}
diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c
index 0c6802507a79..2dc241689d37 100644
--- a/drivers/net/wan/ixp4xx_hss.c
+++ b/drivers/net/wan/ixp4xx_hss.c
@@ -654,7 +654,7 @@ static int hss_hdlc_poll(struct napi_struct *napi, int budget)
netif_rx_complete(dev, napi);
qmgr_enable_irq(rxq);
if (!qmgr_stat_empty(rxq) &&
- netif_rx_reschedule(dev, napi)) {
+ netif_rx_reschedule(napi)) {
#if DEBUG_RX
printk(KERN_DEBUG "%s: hss_hdlc_poll"
" netif_rx_reschedule succeeded\n",
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 04c139666965..b5db57d2fcf5 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -1065,8 +1065,7 @@ static int eject_installer(struct usb_interface *intf)
/* Find bulk out endpoint */
endpoint = &iface_desc->endpoint[1].desc;
if ((endpoint->bEndpointAddress & USB_TYPE_MASK) == USB_DIR_OUT &&
- (endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
- USB_ENDPOINT_XFER_BULK) {
+ usb_endpoint_xfer_bulk(endpoint)) {
bulk_out_ep = endpoint->bEndpointAddress;
} else {
dev_err(&udev->dev,
diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c
index b55cd23ffdef..737bd9484822 100644
--- a/drivers/oprofile/buffer_sync.c
+++ b/drivers/oprofile/buffer_sync.c
@@ -268,18 +268,6 @@ lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset)
return cookie;
}
-static void increment_tail(struct oprofile_cpu_buffer *b)
-{
- unsigned long new_tail = b->tail_pos + 1;
-
- rmb(); /* be sure fifo pointers are synchromized */
-
- if (new_tail < b->buffer_size)
- b->tail_pos = new_tail;
- else
- b->tail_pos = 0;
-}
-
static unsigned long last_cookie = INVALID_COOKIE;
static void add_cpu_switch(int i)
@@ -331,28 +319,25 @@ static void add_trace_begin(void)
#define IBS_FETCH_CODE_SIZE 2
#define IBS_OP_CODE_SIZE 5
-#define IBS_EIP(offset) \
- (((struct op_sample *)&cpu_buf->buffer[(offset)])->eip)
-#define IBS_EVENT(offset) \
- (((struct op_sample *)&cpu_buf->buffer[(offset)])->event)
/*
* Add IBS fetch and op entries to event buffer
*/
-static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code,
- struct mm_struct *mm)
+static void add_ibs_begin(int cpu, int code, struct mm_struct *mm)
{
unsigned long rip;
int i, count;
unsigned long ibs_cookie = 0;
off_t offset;
+ struct op_sample *sample;
- increment_tail(cpu_buf); /* move to RIP entry */
-
- rip = IBS_EIP(cpu_buf->tail_pos);
+ sample = cpu_buffer_read_entry(cpu);
+ if (!sample)
+ goto Error;
+ rip = sample->eip;
#ifdef __LP64__
- rip += IBS_EVENT(cpu_buf->tail_pos) << 32;
+ rip += sample->event << 32;
#endif
if (mm) {
@@ -376,8 +361,8 @@ static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code,
add_event_entry(offset); /* Offset from Dcookie */
/* we send the Dcookie offset, but send the raw Linear Add also*/
- add_event_entry(IBS_EIP(cpu_buf->tail_pos));
- add_event_entry(IBS_EVENT(cpu_buf->tail_pos));
+ add_event_entry(sample->eip);
+ add_event_entry(sample->event);
if (code == IBS_FETCH_CODE)
count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/
@@ -385,10 +370,17 @@ static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code,
count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/
for (i = 0; i < count; i++) {
- increment_tail(cpu_buf);
- add_event_entry(IBS_EIP(cpu_buf->tail_pos));
- add_event_entry(IBS_EVENT(cpu_buf->tail_pos));
+ sample = cpu_buffer_read_entry(cpu);
+ if (!sample)
+ goto Error;
+ add_event_entry(sample->eip);
+ add_event_entry(sample->event);
}
+
+ return;
+
+Error:
+ return;
}
#endif
@@ -466,33 +458,6 @@ static inline int is_code(unsigned long val)
}
-/* "acquire" as many cpu buffer slots as we can */
-static unsigned long get_slots(struct oprofile_cpu_buffer *b)
-{
- unsigned long head = b->head_pos;
- unsigned long tail = b->tail_pos;
-
- /*
- * Subtle. This resets the persistent last_task
- * and in_kernel values used for switching notes.
- * BUT, there is a small window between reading
- * head_pos, and this call, that means samples
- * can appear at the new head position, but not
- * be prefixed with the notes for switching
- * kernel mode or a task switch. This small hole
- * can lead to mis-attribution or samples where
- * we don't know if it's in the kernel or not,
- * at the start of an event buffer.
- */
- cpu_buffer_reset(b);
-
- if (head >= tail)
- return head - tail;
-
- return head + (b->buffer_size - tail);
-}
-
-
/* Move tasks along towards death. Any tasks on dead_tasks
* will definitely have no remaining references in any
* CPU buffers at this point, because we use two lists,
@@ -559,61 +524,61 @@ typedef enum {
*/
void sync_buffer(int cpu)
{
- struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu);
struct mm_struct *mm = NULL;
+ struct mm_struct *oldmm;
struct task_struct *new;
unsigned long cookie = 0;
int in_kernel = 1;
sync_buffer_state state = sb_buffer_start;
-#ifndef CONFIG_OPROFILE_IBS
unsigned int i;
unsigned long available;
-#endif
mutex_lock(&buffer_mutex);
add_cpu_switch(cpu);
- /* Remember, only we can modify tail_pos */
-
-#ifndef CONFIG_OPROFILE_IBS
- available = get_slots(cpu_buf);
+ cpu_buffer_reset(cpu);
+ available = cpu_buffer_entries(cpu);
for (i = 0; i < available; ++i) {
-#else
- while (get_slots(cpu_buf)) {
-#endif
- struct op_sample *s = &cpu_buf->buffer[cpu_buf->tail_pos];
+ struct op_sample *s = cpu_buffer_read_entry(cpu);
+ if (!s)
+ break;
if (is_code(s->eip)) {
- if (s->event <= CPU_IS_KERNEL) {
+ switch (s->event) {
+ case 0:
+ case CPU_IS_KERNEL:
/* kernel/userspace switch */
in_kernel = s->event;
if (state == sb_buffer_start)
state = sb_sample_start;
add_kernel_ctx_switch(s->event);
- } else if (s->event == CPU_TRACE_BEGIN) {
+ break;
+ case CPU_TRACE_BEGIN:
state = sb_bt_start;
add_trace_begin();
+ break;
#ifdef CONFIG_OPROFILE_IBS
- } else if (s->event == IBS_FETCH_BEGIN) {
+ case IBS_FETCH_BEGIN:
state = sb_bt_start;
- add_ibs_begin(cpu_buf, IBS_FETCH_CODE, mm);
- } else if (s->event == IBS_OP_BEGIN) {
+ add_ibs_begin(cpu, IBS_FETCH_CODE, mm);
+ break;
+ case IBS_OP_BEGIN:
state = sb_bt_start;
- add_ibs_begin(cpu_buf, IBS_OP_CODE, mm);
+ add_ibs_begin(cpu, IBS_OP_CODE, mm);
+ break;
#endif
- } else {
- struct mm_struct *oldmm = mm;
-
+ default:
/* userspace context switch */
+ oldmm = mm;
new = (struct task_struct *)s->event;
-
release_mm(oldmm);
mm = take_tasks_mm(new);
if (mm != oldmm)
cookie = get_exec_dcookie(mm);
add_user_ctx_switch(new, cookie);
+ break;
}
} else if (state >= sb_bt_start &&
!add_sample(mm, s, in_kernel)) {
@@ -622,8 +587,6 @@ void sync_buffer(int cpu)
atomic_inc(&oprofile_stats.bt_lost_no_mapping);
}
}
-
- increment_tail(cpu_buf);
}
release_mm(mm);
diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c
index 01d38e78cde1..61090969158f 100644
--- a/drivers/oprofile/cpu_buffer.c
+++ b/drivers/oprofile/cpu_buffer.c
@@ -28,6 +28,25 @@
#include "buffer_sync.h"
#include "oprof.h"
+#define OP_BUFFER_FLAGS 0
+
+/*
+ * Read and write access is using spin locking. Thus, writing to the
+ * buffer by NMI handler (x86) could occur also during critical
+ * sections when reading the buffer. To avoid this, there are 2
+ * buffers for independent read and write access. Read access is in
+ * process context only, write access only in the NMI handler. If the
+ * read buffer runs empty, both buffers are swapped atomically. There
+ * is potentially a small window during swapping where the buffers are
+ * disabled and samples could be lost.
+ *
+ * Using 2 buffers is a little bit overhead, but the solution is clear
+ * and does not require changes in the ring buffer implementation. It
+ * can be changed to a single buffer solution when the ring buffer
+ * access is implemented as non-locking atomic code.
+ */
+struct ring_buffer *op_ring_buffer_read;
+struct ring_buffer *op_ring_buffer_write;
DEFINE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer);
static void wq_sync_buffer(struct work_struct *work);
@@ -37,12 +56,12 @@ static int work_enabled;
void free_cpu_buffers(void)
{
- int i;
-
- for_each_possible_cpu(i) {
- vfree(per_cpu(cpu_buffer, i).buffer);
- per_cpu(cpu_buffer, i).buffer = NULL;
- }
+ if (op_ring_buffer_read)
+ ring_buffer_free(op_ring_buffer_read);
+ op_ring_buffer_read = NULL;
+ if (op_ring_buffer_write)
+ ring_buffer_free(op_ring_buffer_write);
+ op_ring_buffer_write = NULL;
}
unsigned long oprofile_get_cpu_buffer_size(void)
@@ -64,14 +83,16 @@ int alloc_cpu_buffers(void)
unsigned long buffer_size = fs_cpu_buffer_size;
+ op_ring_buffer_read = ring_buffer_alloc(buffer_size, OP_BUFFER_FLAGS);
+ if (!op_ring_buffer_read)
+ goto fail;
+ op_ring_buffer_write = ring_buffer_alloc(buffer_size, OP_BUFFER_FLAGS);
+ if (!op_ring_buffer_write)
+ goto fail;
+
for_each_possible_cpu(i) {
struct oprofile_cpu_buffer *b = &per_cpu(cpu_buffer, i);
- b->buffer = vmalloc_node(sizeof(struct op_sample) * buffer_size,
- cpu_to_node(i));
- if (!b->buffer)
- goto fail;
-
b->last_task = NULL;
b->last_is_kernel = -1;
b->tracing = 0;
@@ -124,57 +145,31 @@ void end_cpu_work(void)
flush_scheduled_work();
}
-/* Resets the cpu buffer to a sane state. */
-void cpu_buffer_reset(struct oprofile_cpu_buffer *cpu_buf)
-{
- /* reset these to invalid values; the next sample
- * collected will populate the buffer with proper
- * values to initialize the buffer
- */
- cpu_buf->last_is_kernel = -1;
- cpu_buf->last_task = NULL;
-}
-
-/* compute number of available slots in cpu_buffer queue */
-static unsigned long nr_available_slots(struct oprofile_cpu_buffer const *b)
+static inline int
+add_sample(struct oprofile_cpu_buffer *cpu_buf,
+ unsigned long pc, unsigned long event)
{
- unsigned long head = b->head_pos;
- unsigned long tail = b->tail_pos;
+ struct op_entry entry;
+ int ret;
- if (tail > head)
- return (tail - head) - 1;
+ ret = cpu_buffer_write_entry(&entry);
+ if (ret)
+ return ret;
- return tail + (b->buffer_size - head) - 1;
-}
+ entry.sample->eip = pc;
+ entry.sample->event = event;
-static void increment_head(struct oprofile_cpu_buffer *b)
-{
- unsigned long new_head = b->head_pos + 1;
-
- /* Ensure anything written to the slot before we
- * increment is visible */
- wmb();
-
- if (new_head < b->buffer_size)
- b->head_pos = new_head;
- else
- b->head_pos = 0;
-}
+ ret = cpu_buffer_write_commit(&entry);
+ if (ret)
+ return ret;
-static inline void
-add_sample(struct oprofile_cpu_buffer *cpu_buf,
- unsigned long pc, unsigned long event)
-{
- struct op_sample *entry = &cpu_buf->buffer[cpu_buf->head_pos];
- entry->eip = pc;
- entry->event = event;
- increment_head(cpu_buf);
+ return 0;
}
-static inline void
+static inline int
add_code(struct oprofile_cpu_buffer *buffer, unsigned long value)
{
- add_sample(buffer, ESCAPE_CODE, value);
+ return add_sample(buffer, ESCAPE_CODE, value);
}
/* This must be safe from any context. It's safe writing here
@@ -198,11 +193,6 @@ static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc,
return 0;
}
- if (nr_available_slots(cpu_buf) < 3) {
- cpu_buf->sample_lost_overflow++;
- return 0;
- }
-
is_kernel = !!is_kernel;
task = current;
@@ -210,26 +200,29 @@ static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc,
/* notice a switch from user->kernel or vice versa */
if (cpu_buf->last_is_kernel != is_kernel) {
cpu_buf->last_is_kernel = is_kernel;
- add_code(cpu_buf, is_kernel);
+ if (add_code(cpu_buf, is_kernel))
+ goto fail;
}
/* notice a task switch */
if (cpu_buf->last_task != task) {
cpu_buf->last_task = task;
- add_code(cpu_buf, (unsigned long)task);
+ if (add_code(cpu_buf, (unsigned long)task))
+ goto fail;
}
- add_sample(cpu_buf, pc, event);
+ if (add_sample(cpu_buf, pc, event))
+ goto fail;
+
return 1;
+
+fail:
+ cpu_buf->sample_lost_overflow++;
+ return 0;
}
static int oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf)
{
- if (nr_available_slots(cpu_buf) < 4) {
- cpu_buf->sample_lost_overflow++;
- return 0;
- }
-
add_code(cpu_buf, CPU_TRACE_BEGIN);
cpu_buf->tracing = 1;
return 1;
@@ -253,8 +246,10 @@ void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs,
if (!oprofile_begin_trace(cpu_buf))
return;
- /* if log_sample() fail we can't backtrace since we lost the source
- * of this event */
+ /*
+ * if log_sample() fail we can't backtrace since we lost the
+ * source of this event
+ */
if (log_sample(cpu_buf, pc, is_kernel, event))
oprofile_ops.backtrace(regs, backtrace_depth);
oprofile_end_trace(cpu_buf);
@@ -272,49 +267,55 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event)
#define MAX_IBS_SAMPLE_SIZE 14
-void oprofile_add_ibs_sample(struct pt_regs *const regs,
- unsigned int *const ibs_sample, int ibs_code)
+void oprofile_add_ibs_sample(struct pt_regs * const regs,
+ unsigned int * const ibs_sample, int ibs_code)
{
int is_kernel = !user_mode(regs);
struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer);
struct task_struct *task;
+ int fail = 0;
cpu_buf->sample_received++;
- if (nr_available_slots(cpu_buf) < MAX_IBS_SAMPLE_SIZE) {
- /* we can't backtrace since we lost the source of this event */
- cpu_buf->sample_lost_overflow++;
- return;
- }
-
/* notice a switch from user->kernel or vice versa */
if (cpu_buf->last_is_kernel != is_kernel) {
+ if (add_code(cpu_buf, is_kernel))
+ goto fail;
cpu_buf->last_is_kernel = is_kernel;
- add_code(cpu_buf, is_kernel);
}
/* notice a task switch */
if (!is_kernel) {
task = current;
if (cpu_buf->last_task != task) {
+ if (add_code(cpu_buf, (unsigned long)task))
+ goto fail;
cpu_buf->last_task = task;
- add_code(cpu_buf, (unsigned long)task);
}
}
- add_code(cpu_buf, ibs_code);
- add_sample(cpu_buf, ibs_sample[0], ibs_sample[1]);
- add_sample(cpu_buf, ibs_sample[2], ibs_sample[3]);
- add_sample(cpu_buf, ibs_sample[4], ibs_sample[5]);
+ fail = fail || add_code(cpu_buf, ibs_code);
+ fail = fail || add_sample(cpu_buf, ibs_sample[0], ibs_sample[1]);
+ fail = fail || add_sample(cpu_buf, ibs_sample[2], ibs_sample[3]);
+ fail = fail || add_sample(cpu_buf, ibs_sample[4], ibs_sample[5]);
if (ibs_code == IBS_OP_BEGIN) {
- add_sample(cpu_buf, ibs_sample[6], ibs_sample[7]);
- add_sample(cpu_buf, ibs_sample[8], ibs_sample[9]);
- add_sample(cpu_buf, ibs_sample[10], ibs_sample[11]);
+ fail = fail || add_sample(cpu_buf, ibs_sample[6], ibs_sample[7]);
+ fail = fail || add_sample(cpu_buf, ibs_sample[8], ibs_sample[9]);
+ fail = fail || add_sample(cpu_buf, ibs_sample[10], ibs_sample[11]);
}
+ if (fail)
+ goto fail;
+
if (backtrace_depth)
oprofile_ops.backtrace(regs, backtrace_depth);
+
+ return;
+
+fail:
+ cpu_buf->sample_lost_overflow++;
+ return;
}
#endif
@@ -332,21 +333,21 @@ void oprofile_add_trace(unsigned long pc)
if (!cpu_buf->tracing)
return;
- if (nr_available_slots(cpu_buf) < 1) {
- cpu_buf->tracing = 0;
- cpu_buf->sample_lost_overflow++;
- return;
- }
+ /*
+ * broken frame can give an eip with the same value as an
+ * escape code, abort the trace if we get it
+ */
+ if (pc == ESCAPE_CODE)
+ goto fail;
- /* broken frame can give an eip with the same value as an escape code,
- * abort the trace if we get it */
- if (pc == ESCAPE_CODE) {
- cpu_buf->tracing = 0;
- cpu_buf->backtrace_aborted++;
- return;
- }
+ if (add_sample(cpu_buf, pc, 0))
+ goto fail;
- add_sample(cpu_buf, pc, 0);
+ return;
+fail:
+ cpu_buf->tracing = 0;
+ cpu_buf->backtrace_aborted++;
+ return;
}
/*
diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h
index d3cc26264db5..aacb0f0bc566 100644
--- a/drivers/oprofile/cpu_buffer.h
+++ b/drivers/oprofile/cpu_buffer.h
@@ -15,6 +15,7 @@
#include <linux/workqueue.h>
#include <linux/cache.h>
#include <linux/sched.h>
+#include <linux/ring_buffer.h>
struct task_struct;
@@ -32,6 +33,12 @@ struct op_sample {
unsigned long event;
};
+struct op_entry {
+ struct ring_buffer_event *event;
+ struct op_sample *sample;
+ unsigned long irq_flags;
+};
+
struct oprofile_cpu_buffer {
volatile unsigned long head_pos;
volatile unsigned long tail_pos;
@@ -39,7 +46,6 @@ struct oprofile_cpu_buffer {
struct task_struct *last_task;
int last_is_kernel;
int tracing;
- struct op_sample *buffer;
unsigned long sample_received;
unsigned long sample_lost_overflow;
unsigned long backtrace_aborted;
@@ -48,9 +54,68 @@ struct oprofile_cpu_buffer {
struct delayed_work work;
};
+extern struct ring_buffer *op_ring_buffer_read;
+extern struct ring_buffer *op_ring_buffer_write;
DECLARE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer);
-void cpu_buffer_reset(struct oprofile_cpu_buffer *cpu_buf);
+/*
+ * Resets the cpu buffer to a sane state.
+ *
+ * reset these to invalid values; the next sample collected will
+ * populate the buffer with proper values to initialize the buffer
+ */
+static inline void cpu_buffer_reset(int cpu)
+{
+ struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu);
+
+ cpu_buf->last_is_kernel = -1;
+ cpu_buf->last_task = NULL;
+}
+
+static inline int cpu_buffer_write_entry(struct op_entry *entry)
+{
+ entry->event = ring_buffer_lock_reserve(op_ring_buffer_write,
+ sizeof(struct op_sample),
+ &entry->irq_flags);
+ if (entry->event)
+ entry->sample = ring_buffer_event_data(entry->event);
+ else
+ entry->sample = NULL;
+
+ if (!entry->sample)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static inline int cpu_buffer_write_commit(struct op_entry *entry)
+{
+ return ring_buffer_unlock_commit(op_ring_buffer_write, entry->event,
+ entry->irq_flags);
+}
+
+static inline struct op_sample *cpu_buffer_read_entry(int cpu)
+{
+ struct ring_buffer_event *e;
+ e = ring_buffer_consume(op_ring_buffer_read, cpu, NULL);
+ if (e)
+ return ring_buffer_event_data(e);
+ if (ring_buffer_swap_cpu(op_ring_buffer_read,
+ op_ring_buffer_write,
+ cpu))
+ return NULL;
+ e = ring_buffer_consume(op_ring_buffer_read, cpu, NULL);
+ if (e)
+ return ring_buffer_event_data(e);
+ return NULL;
+}
+
+/* "acquire" as many cpu buffer slots as we can */
+static inline unsigned long cpu_buffer_entries(int cpu)
+{
+ return ring_buffer_entries_cpu(op_ring_buffer_read, cpu)
+ + ring_buffer_entries_cpu(op_ring_buffer_write, cpu);
+}
/* transient events for the CPU buffer -> event buffer */
#define CPU_IS_KERNEL 1
diff --git a/drivers/oprofile/oprofile_files.c b/drivers/oprofile/oprofile_files.c
index cc106d503ace..d8201998b0b7 100644
--- a/drivers/oprofile/oprofile_files.c
+++ b/drivers/oprofile/oprofile_files.c
@@ -14,9 +14,13 @@
#include "oprofile_stats.h"
#include "oprof.h"
-unsigned long fs_buffer_size = 131072;
-unsigned long fs_cpu_buffer_size = 8192;
-unsigned long fs_buffer_watershed = 32768; /* FIXME: tune */
+#define FS_BUFFER_SIZE_DEFAULT 131072
+#define FS_CPU_BUFFER_SIZE_DEFAULT 8192
+#define FS_BUFFER_WATERSHED_DEFAULT 32768 /* FIXME: tune */
+
+unsigned long fs_buffer_size;
+unsigned long fs_cpu_buffer_size;
+unsigned long fs_buffer_watershed;
static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
@@ -120,6 +124,11 @@ static const struct file_operations dump_fops = {
void oprofile_create_files(struct super_block *sb, struct dentry *root)
{
+ /* reinitialize default values */
+ fs_buffer_size = FS_BUFFER_SIZE_DEFAULT;
+ fs_cpu_buffer_size = FS_CPU_BUFFER_SIZE_DEFAULT;
+ fs_buffer_watershed = FS_BUFFER_WATERSHED_DEFAULT;
+
oprofilefs_create_file(sb, root, "enable", &enable_fops);
oprofilefs_create_file_perm(sb, root, "dump", &dump_fops, 0666);
oprofilefs_create_file(sb, root, "buffer", &event_buffer_fops);
diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c
index 2de5a3238c94..f78371b22529 100644
--- a/drivers/pci/intr_remapping.c
+++ b/drivers/pci/intr_remapping.c
@@ -5,6 +5,7 @@
#include <linux/pci.h>
#include <linux/irq.h>
#include <asm/io_apic.h>
+#include <asm/smp.h>
#include <linux/intel-iommu.h>
#include "intr_remapping.h"
@@ -19,17 +20,75 @@ struct irq_2_iommu {
u8 irte_mask;
};
-static struct irq_2_iommu irq_2_iommuX[NR_IRQS];
+#ifdef CONFIG_SPARSE_IRQ
+static struct irq_2_iommu *get_one_free_irq_2_iommu(int cpu)
+{
+ struct irq_2_iommu *iommu;
+ int node;
+
+ node = cpu_to_node(cpu);
+
+ iommu = kzalloc_node(sizeof(*iommu), GFP_ATOMIC, node);
+ printk(KERN_DEBUG "alloc irq_2_iommu on cpu %d node %d\n", cpu, node);
+
+ return iommu;
+}
static struct irq_2_iommu *irq_2_iommu(unsigned int irq)
{
- return (irq < nr_irqs) ? irq_2_iommuX + irq : NULL;
+ struct irq_desc *desc;
+
+ desc = irq_to_desc(irq);
+
+ if (WARN_ON_ONCE(!desc))
+ return NULL;
+
+ return desc->irq_2_iommu;
+}
+
+static struct irq_2_iommu *irq_2_iommu_alloc_cpu(unsigned int irq, int cpu)
+{
+ struct irq_desc *desc;
+ struct irq_2_iommu *irq_iommu;
+
+ /*
+ * alloc irq desc if not allocated already.
+ */
+ desc = irq_to_desc_alloc_cpu(irq, cpu);
+ if (!desc) {
+ printk(KERN_INFO "can not get irq_desc for %d\n", irq);
+ return NULL;
+ }
+
+ irq_iommu = desc->irq_2_iommu;
+
+ if (!irq_iommu)
+ desc->irq_2_iommu = get_one_free_irq_2_iommu(cpu);
+
+ return desc->irq_2_iommu;
}
static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq)
{
+ return irq_2_iommu_alloc_cpu(irq, boot_cpu_id);
+}
+
+#else /* !CONFIG_SPARSE_IRQ */
+
+static struct irq_2_iommu irq_2_iommuX[NR_IRQS];
+
+static struct irq_2_iommu *irq_2_iommu(unsigned int irq)
+{
+ if (irq < nr_irqs)
+ return &irq_2_iommuX[irq];
+
+ return NULL;
+}
+static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq)
+{
return irq_2_iommu(irq);
}
+#endif
static DEFINE_SPINLOCK(irq_2_ir_lock);
@@ -86,9 +145,11 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
if (!count)
return -1;
+#ifndef CONFIG_SPARSE_IRQ
/* protect irq_2_iommu_alloc later */
if (irq >= nr_irqs)
return -1;
+#endif
/*
* start the IRTE search from index 0.
@@ -130,6 +191,12 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
table->base[i].present = 1;
irq_iommu = irq_2_iommu_alloc(irq);
+ if (!irq_iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ printk(KERN_ERR "can't allocate irq_2_iommu\n");
+ return -1;
+ }
+
irq_iommu->iommu = iommu;
irq_iommu->irte_index = index;
irq_iommu->sub_handle = 0;
@@ -177,6 +244,12 @@ int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle)
irq_iommu = irq_2_iommu_alloc(irq);
+ if (!irq_iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ printk(KERN_ERR "can't allocate irq_2_iommu\n");
+ return -1;
+ }
+
irq_iommu->iommu = iommu;
irq_iommu->irte_index = index;
irq_iommu->sub_handle = subhandle;
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 74801f7df9c9..11a51f8ed3b3 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -103,11 +103,11 @@ static void msix_set_enable(struct pci_dev *dev, int enable)
}
}
-static void msix_flush_writes(unsigned int irq)
+static void msix_flush_writes(struct irq_desc *desc)
{
struct msi_desc *entry;
- entry = get_irq_msi(irq);
+ entry = get_irq_desc_msi(desc);
BUG_ON(!entry || !entry->dev);
switch (entry->msi_attrib.type) {
case PCI_CAP_ID_MSI:
@@ -135,11 +135,11 @@ static void msix_flush_writes(unsigned int irq)
* Returns 1 if it succeeded in masking the interrupt and 0 if the device
* doesn't support MSI masking.
*/
-static int msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag)
+static int msi_set_mask_bits(struct irq_desc *desc, u32 mask, u32 flag)
{
struct msi_desc *entry;
- entry = get_irq_msi(irq);
+ entry = get_irq_desc_msi(desc);
BUG_ON(!entry || !entry->dev);
switch (entry->msi_attrib.type) {
case PCI_CAP_ID_MSI:
@@ -172,9 +172,9 @@ static int msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag)
return 1;
}
-void read_msi_msg(unsigned int irq, struct msi_msg *msg)
+void read_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg)
{
- struct msi_desc *entry = get_irq_msi(irq);
+ struct msi_desc *entry = get_irq_desc_msi(desc);
switch(entry->msi_attrib.type) {
case PCI_CAP_ID_MSI:
{
@@ -211,9 +211,16 @@ void read_msi_msg(unsigned int irq, struct msi_msg *msg)
}
}
-void write_msi_msg(unsigned int irq, struct msi_msg *msg)
+void read_msi_msg(unsigned int irq, struct msi_msg *msg)
{
- struct msi_desc *entry = get_irq_msi(irq);
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ read_msi_msg_desc(desc, msg);
+}
+
+void write_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg)
+{
+ struct msi_desc *entry = get_irq_desc_msi(desc);
switch (entry->msi_attrib.type) {
case PCI_CAP_ID_MSI:
{
@@ -252,21 +259,31 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg)
entry->msg = *msg;
}
+void write_msi_msg(unsigned int irq, struct msi_msg *msg)
+{
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ write_msi_msg_desc(desc, msg);
+}
+
void mask_msi_irq(unsigned int irq)
{
- msi_set_mask_bits(irq, 1, 1);
- msix_flush_writes(irq);
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ msi_set_mask_bits(desc, 1, 1);
+ msix_flush_writes(desc);
}
void unmask_msi_irq(unsigned int irq)
{
- msi_set_mask_bits(irq, 1, 0);
- msix_flush_writes(irq);
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ msi_set_mask_bits(desc, 1, 0);
+ msix_flush_writes(desc);
}
static int msi_free_irqs(struct pci_dev* dev);
-
static struct msi_desc* alloc_msi_entry(void)
{
struct msi_desc *entry;
@@ -303,9 +320,11 @@ static void __pci_restore_msi_state(struct pci_dev *dev)
pci_intx_for_msi(dev, 0);
msi_set_enable(dev, 0);
write_msi_msg(dev->irq, &entry->msg);
- if (entry->msi_attrib.maskbit)
- msi_set_mask_bits(dev->irq, entry->msi_attrib.maskbits_mask,
+ if (entry->msi_attrib.maskbit) {
+ struct irq_desc *desc = irq_to_desc(dev->irq);
+ msi_set_mask_bits(desc, entry->msi_attrib.maskbits_mask,
entry->msi_attrib.masked);
+ }
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
control &= ~PCI_MSI_FLAGS_QSIZE;
@@ -327,8 +346,9 @@ static void __pci_restore_msix_state(struct pci_dev *dev)
msix_set_enable(dev, 0);
list_for_each_entry(entry, &dev->msi_list, list) {
+ struct irq_desc *desc = irq_to_desc(entry->irq);
write_msi_msg(entry->irq, &entry->msg);
- msi_set_mask_bits(entry->irq, 1, entry->msi_attrib.masked);
+ msi_set_mask_bits(desc, 1, entry->msi_attrib.masked);
}
BUG_ON(list_empty(&dev->msi_list));
@@ -596,7 +616,8 @@ void pci_msi_shutdown(struct pci_dev* dev)
/* Return the the pci reset with msi irqs unmasked */
if (entry->msi_attrib.maskbit) {
u32 mask = entry->msi_attrib.maskbits_mask;
- msi_set_mask_bits(dev->irq, mask, ~mask);
+ struct irq_desc *desc = irq_to_desc(dev->irq);
+ msi_set_mask_bits(desc, mask, ~mask);
}
if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI)
return;
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 222904411a13..276473543982 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -217,7 +217,7 @@ config PCMCIA_PXA2XX
depends on ARM && ARCH_PXA && PCMCIA
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
|| MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
- || ARCH_VIPER)
+ || ARCH_VIPER || ARCH_PXA_ESERIES)
help
Say Y here to include support for the PXA2xx PCMCIA controller
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index 238629ad7f7c..bbac46327227 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -72,5 +72,6 @@ pxa2xx-obj-$(CONFIG_ARCH_VIPER) += pxa2xx_viper.o
pxa2xx-obj-$(CONFIG_TRIZEPS_PCMCIA) += pxa2xx_trizeps4.o
pxa2xx-obj-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o
pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o
+pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o
obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_core.o $(pxa2xx-obj-y)
diff --git a/drivers/pcmcia/pxa2xx_e740.c b/drivers/pcmcia/pxa2xx_e740.c
new file mode 100644
index 000000000000..f663a011bf4a
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_e740.c
@@ -0,0 +1,176 @@
+/*
+ * Toshiba e740 PCMCIA specific routines.
+ *
+ * (c) 2004 Ian Molton <spyro@f2s.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/eseries-gpio.h>
+
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include "soc_common.h"
+
+static struct pcmcia_irqs cd_irqs[] = {
+ {
+ .sock = 0,
+ .irq = IRQ_GPIO(GPIO_E740_PCMCIA_CD0),
+ .str = "CF card detect"
+ },
+ {
+ .sock = 1,
+ .irq = IRQ_GPIO(GPIO_E740_PCMCIA_CD1),
+ .str = "Wifi switch"
+ },
+};
+
+static int e740_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ skt->irq = skt->nr == 0 ? IRQ_GPIO(GPIO_E740_PCMCIA_RDY0) :
+ IRQ_GPIO(GPIO_E740_PCMCIA_RDY1);
+
+ return soc_pcmcia_request_irqs(skt, &cd_irqs[skt->nr], 1);
+}
+
+/*
+ * Release all resources.
+ */
+static void e740_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_free_irqs(skt, &cd_irqs[skt->nr], 1);
+}
+
+static void e740_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ if (skt->nr == 0) {
+ state->detect = gpio_get_value(GPIO_E740_PCMCIA_CD0) ? 0 : 1;
+ state->ready = gpio_get_value(GPIO_E740_PCMCIA_RDY0) ? 1 : 0;
+ } else {
+ state->detect = gpio_get_value(GPIO_E740_PCMCIA_CD1) ? 0 : 1;
+ state->ready = gpio_get_value(GPIO_E740_PCMCIA_RDY1) ? 1 : 0;
+ }
+
+ state->vs_3v = 1;
+ state->bvd1 = 1;
+ state->bvd2 = 1;
+ state->wrprot = 0;
+ state->vs_Xv = 0;
+}
+
+static int e740_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ if (state->flags & SS_RESET) {
+ if (skt->nr == 0)
+ gpio_set_value(GPIO_E740_PCMCIA_RST0, 1);
+ else
+ gpio_set_value(GPIO_E740_PCMCIA_RST1, 1);
+ } else {
+ if (skt->nr == 0)
+ gpio_set_value(GPIO_E740_PCMCIA_RST0, 0);
+ else
+ gpio_set_value(GPIO_E740_PCMCIA_RST1, 0);
+ }
+
+ switch (state->Vcc) {
+ case 0: /* Socket off */
+ if (skt->nr == 0)
+ gpio_set_value(GPIO_E740_PCMCIA_PWR0, 0);
+ else
+ gpio_set_value(GPIO_E740_PCMCIA_PWR1, 1);
+ break;
+ case 50:
+ case 33: /* socket on */
+ if (skt->nr == 0)
+ gpio_set_value(GPIO_E740_PCMCIA_PWR0, 1);
+ else
+ gpio_set_value(GPIO_E740_PCMCIA_PWR1, 0);
+ break;
+ default:
+ printk(KERN_ERR "e740_cs: Unsupported Vcc: %d\n", state->Vcc);
+ }
+
+ return 0;
+}
+
+/*
+ * Enable card status IRQs on (re-)initialisation. This can
+ * be called at initialisation, power management event, or
+ * pcmcia event.
+ */
+static void e740_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_enable_irqs(skt, cd_irqs, ARRAY_SIZE(cd_irqs));
+}
+
+/*
+ * Disable card status IRQs on suspend.
+ */
+static void e740_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_disable_irqs(skt, cd_irqs, ARRAY_SIZE(cd_irqs));
+}
+
+static struct pcmcia_low_level e740_pcmcia_ops = {
+ .owner = THIS_MODULE,
+ .hw_init = e740_pcmcia_hw_init,
+ .hw_shutdown = e740_pcmcia_hw_shutdown,
+ .socket_state = e740_pcmcia_socket_state,
+ .configure_socket = e740_pcmcia_configure_socket,
+ .socket_init = e740_pcmcia_socket_init,
+ .socket_suspend = e740_pcmcia_socket_suspend,
+ .nr = 2,
+};
+
+static struct platform_device *e740_pcmcia_device;
+
+static int __init e740_pcmcia_init(void)
+{
+ int ret;
+
+ if (!machine_is_e740())
+ return -ENODEV;
+
+ e740_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
+ if (!e740_pcmcia_device)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(e740_pcmcia_device, &e740_pcmcia_ops,
+ sizeof(e740_pcmcia_ops));
+
+ if (!ret)
+ ret = platform_device_add(e740_pcmcia_device);
+
+ if (ret)
+ platform_device_put(e740_pcmcia_device);
+
+ return ret;
+}
+
+static void __exit e740_pcmcia_exit(void)
+{
+ platform_device_unregister(e740_pcmcia_device);
+}
+
+module_init(e740_pcmcia_init);
+module_exit(e740_pcmcia_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
+MODULE_ALIAS("platform:pxa2xx-pcmcia");
+MODULE_DESCRIPTION("e740 PCMCIA platform support");
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c
index 2133f37906f2..d5e4e637ddec 100644
--- a/drivers/rtc/rtc-at91sam9.c
+++ b/drivers/rtc/rtc-at91sam9.c
@@ -21,6 +21,7 @@
#include <mach/board.h>
#include <mach/at91_rtt.h>
+#include <mach/cpu.h>
/*
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index f59277bbedaa..7a568beba3f0 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -26,7 +26,7 @@
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/plat-s3c/regs-rtc.h>
+#include <plat/regs-rtc.h>
/* I have yet to find an S3C implementation with more than one
* of these rtc blocks in */
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
index 66a9bb85bbe8..d26a5f82aaba 100644
--- a/drivers/rtc/rtc-sa1100.c
+++ b/drivers/rtc/rtc-sa1100.c
@@ -38,11 +38,11 @@
#include <mach/pxa-regs.h>
#endif
-#define TIMER_FREQ CLOCK_TICK_RATE
#define RTC_DEF_DIVIDER 32768 - 1
#define RTC_DEF_TRIM 0
static unsigned long rtc_freq = 1024;
+static unsigned long timer_freq;
static struct rtc_time rtc_alarm;
static DEFINE_SPINLOCK(sa1100_rtc_lock);
@@ -157,7 +157,7 @@ static irqreturn_t timer1_interrupt(int irq, void *dev_id)
rtc_update_irq(rtc, rtc_timer1_count, RTC_PF | RTC_IRQF);
if (rtc_timer1_count == 1)
- rtc_timer1_count = (rtc_freq * ((1<<30)/(TIMER_FREQ>>2)));
+ rtc_timer1_count = (rtc_freq * ((1 << 30) / (timer_freq >> 2)));
return IRQ_HANDLED;
}
@@ -166,7 +166,7 @@ static int sa1100_rtc_read_callback(struct device *dev, int data)
{
if (data & RTC_PF) {
/* interpolate missed periods and set match for the next */
- unsigned long period = TIMER_FREQ/rtc_freq;
+ unsigned long period = timer_freq / rtc_freq;
unsigned long oscr = OSCR;
unsigned long osmr1 = OSMR1;
unsigned long missed = (oscr - osmr1)/period;
@@ -263,7 +263,7 @@ static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd,
return 0;
case RTC_PIE_ON:
spin_lock_irq(&sa1100_rtc_lock);
- OSMR1 = TIMER_FREQ/rtc_freq + OSCR;
+ OSMR1 = timer_freq / rtc_freq + OSCR;
OIER |= OIER_E1;
rtc_timer1_count = 1;
spin_unlock_irq(&sa1100_rtc_lock);
@@ -271,7 +271,7 @@ static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd,
case RTC_IRQP_READ:
return put_user(rtc_freq, (unsigned long *)arg);
case RTC_IRQP_SET:
- if (arg < 1 || arg > TIMER_FREQ)
+ if (arg < 1 || arg > timer_freq)
return -EINVAL;
rtc_freq = arg;
return 0;
@@ -352,6 +352,8 @@ static int sa1100_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
+ timer_freq = get_clock_tick_rate();
+
/*
* According to the manual we should be able to let RTTR be zero
* and then a default diviser for a 32.768KHz clock is used.
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 3d442444c618..28c90b89f2b4 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -188,11 +188,13 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev,
config = kvm_vq_config(kdev->desc)+index;
err = vmem_add_mapping(config->address,
- vring_size(config->num, PAGE_SIZE));
+ vring_size(config->num,
+ KVM_S390_VIRTIO_RING_ALIGN));
if (err)
goto out;
- vq = vring_new_virtqueue(config->num, vdev, (void *) config->address,
+ vq = vring_new_virtqueue(config->num, KVM_S390_VIRTIO_RING_ALIGN,
+ vdev, (void *) config->address,
kvm_notify, callback);
if (!vq) {
err = -ENOMEM;
@@ -209,7 +211,8 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev,
return vq;
unmap:
vmem_remove_mapping(config->address,
- vring_size(config->num, PAGE_SIZE));
+ vring_size(config->num,
+ KVM_S390_VIRTIO_RING_ALIGN));
out:
return ERR_PTR(err);
}
@@ -220,7 +223,8 @@ static void kvm_del_vq(struct virtqueue *vq)
vring_del_virtqueue(vq);
vmem_remove_mapping(config->address,
- vring_size(config->num, PAGE_SIZE));
+ vring_size(config->num,
+ KVM_S390_VIRTIO_RING_ALIGN));
}
/*
@@ -295,13 +299,29 @@ static void scan_devices(void)
*/
static void kvm_extint_handler(u16 code)
{
- void *data = (void *) *(long *) __LC_PFAULT_INTPARM;
- u16 subcode = S390_lowcore.cpu_addr;
+ struct virtqueue *vq;
+ u16 subcode;
+ int config_changed;
+ subcode = S390_lowcore.cpu_addr;
if ((subcode & 0xff00) != VIRTIO_SUBCODE_64)
return;
- vring_interrupt(0, data);
+ /* The LSB might be overloaded, we have to mask it */
+ vq = (struct virtqueue *) ((*(long *) __LC_PFAULT_INTPARM) & ~1UL);
+
+ /* We use the LSB of extparam, to decide, if this interrupt is a config
+ * change or a "standard" interrupt */
+ config_changed = (*(int *) __LC_EXT_PARAMS & 1);
+
+ if (config_changed) {
+ struct virtio_driver *drv;
+ drv = container_of(vq->vdev->dev.driver,
+ struct virtio_driver, driver);
+ if (drv->config_changed)
+ drv->config_changed(vq->vdev);
+ } else
+ vring_interrupt(0, vq);
}
/*
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index e529b55b3ce9..8af7dfbe022c 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -34,13 +34,12 @@
#define ZFCP_BUS_ID_SIZE 20
-static char *device;
-
MODULE_AUTHOR("IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com");
MODULE_DESCRIPTION("FCP HBA driver");
MODULE_LICENSE("GPL");
-module_param(device, charp, 0400);
+static char *init_device;
+module_param_named(device, init_device, charp, 0400);
MODULE_PARM_DESC(device, "specify initial device");
static int zfcp_reqlist_alloc(struct zfcp_adapter *adapter)
@@ -73,46 +72,7 @@ int zfcp_reqlist_isempty(struct zfcp_adapter *adapter)
return 1;
}
-static int __init zfcp_device_setup(char *devstr)
-{
- char *token;
- char *str;
-
- if (!devstr)
- return 0;
-
- /* duplicate devstr and keep the original for sysfs presentation*/
- str = kmalloc(strlen(devstr) + 1, GFP_KERNEL);
- if (!str)
- return 0;
-
- strcpy(str, devstr);
-
- token = strsep(&str, ",");
- if (!token || strlen(token) >= ZFCP_BUS_ID_SIZE)
- goto err_out;
- strncpy(zfcp_data.init_busid, token, ZFCP_BUS_ID_SIZE);
-
- token = strsep(&str, ",");
- if (!token || strict_strtoull(token, 0,
- (unsigned long long *) &zfcp_data.init_wwpn))
- goto err_out;
-
- token = strsep(&str, ",");
- if (!token || strict_strtoull(token, 0,
- (unsigned long long *) &zfcp_data.init_fcp_lun))
- goto err_out;
-
- kfree(str);
- return 1;
-
- err_out:
- kfree(str);
- pr_err("%s is not a valid SCSI device\n", devstr);
- return 0;
-}
-
-static void __init zfcp_init_device_configure(void)
+static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun)
{
struct zfcp_adapter *adapter;
struct zfcp_port *port;
@@ -120,17 +80,17 @@ static void __init zfcp_init_device_configure(void)
down(&zfcp_data.config_sema);
read_lock_irq(&zfcp_data.config_lock);
- adapter = zfcp_get_adapter_by_busid(zfcp_data.init_busid);
+ adapter = zfcp_get_adapter_by_busid(busid);
if (adapter)
zfcp_adapter_get(adapter);
read_unlock_irq(&zfcp_data.config_lock);
if (!adapter)
goto out_adapter;
- port = zfcp_port_enqueue(adapter, zfcp_data.init_wwpn, 0, 0);
+ port = zfcp_port_enqueue(adapter, wwpn, 0, 0);
if (IS_ERR(port))
goto out_port;
- unit = zfcp_unit_enqueue(port, zfcp_data.init_fcp_lun);
+ unit = zfcp_unit_enqueue(port, lun);
if (IS_ERR(unit))
goto out_unit;
up(&zfcp_data.config_sema);
@@ -160,6 +120,42 @@ static struct kmem_cache *zfcp_cache_create(int size, char *name)
return kmem_cache_create(name , size, align, 0, NULL);
}
+static void __init zfcp_init_device_setup(char *devstr)
+{
+ char *token;
+ char *str;
+ char busid[ZFCP_BUS_ID_SIZE];
+ u64 wwpn, lun;
+
+ /* duplicate devstr and keep the original for sysfs presentation*/
+ str = kmalloc(strlen(devstr) + 1, GFP_KERNEL);
+ if (!str)
+ return;
+
+ strcpy(str, devstr);
+
+ token = strsep(&str, ",");
+ if (!token || strlen(token) >= ZFCP_BUS_ID_SIZE)
+ goto err_out;
+ strncpy(busid, token, ZFCP_BUS_ID_SIZE);
+
+ token = strsep(&str, ",");
+ if (!token || strict_strtoull(token, 0, (unsigned long long *) &wwpn))
+ goto err_out;
+
+ token = strsep(&str, ",");
+ if (!token || strict_strtoull(token, 0, (unsigned long long *) &lun))
+ goto err_out;
+
+ kfree(str);
+ zfcp_init_device_configure(busid, wwpn, lun);
+ return;
+
+ err_out:
+ kfree(str);
+ pr_err("%s is not a valid SCSI device\n", devstr);
+}
+
static int __init zfcp_module_init(void)
{
int retval = -ENOMEM;
@@ -181,7 +177,6 @@ static int __init zfcp_module_init(void)
zfcp_data.work_queue = create_singlethread_workqueue("zfcp_wq");
- INIT_LIST_HEAD(&zfcp_data.adapter_list_head);
sema_init(&zfcp_data.config_sema, 1);
rwlock_init(&zfcp_data.config_lock);
@@ -203,10 +198,9 @@ static int __init zfcp_module_init(void)
goto out_ccw_register;
}
- if (zfcp_device_setup(device))
- zfcp_init_device_configure();
-
- goto out;
+ if (init_device)
+ zfcp_init_device_setup(init_device);
+ return 0;
out_ccw_register:
misc_deregister(&zfcp_cfdc_misc);
@@ -527,14 +521,11 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
&zfcp_sysfs_adapter_attrs))
goto sysfs_failed;
- write_lock_irq(&zfcp_data.config_lock);
atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
- list_add_tail(&adapter->list, &zfcp_data.adapter_list_head);
- write_unlock_irq(&zfcp_data.config_lock);
-
zfcp_fc_nameserver_init(adapter);
- return 0;
+ if (!zfcp_adapter_scsi_register(adapter))
+ return 0;
sysfs_failed:
zfcp_adapter_debug_unregister(adapter);
@@ -573,14 +564,7 @@ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
return;
zfcp_adapter_debug_unregister(adapter);
-
- /* remove specified adapter data structure from list */
- write_lock_irq(&zfcp_data.config_lock);
- list_del(&adapter->list);
- write_unlock_irq(&zfcp_data.config_lock);
-
zfcp_qdio_free(adapter);
-
zfcp_free_low_mem_buffers(adapter);
kfree(adapter->req_list);
kfree(adapter->fc_stats);
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index 728147131e1d..285881f07648 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -106,10 +106,6 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
if (retval)
goto out;
- retval = zfcp_adapter_scsi_register(adapter);
- if (retval)
- goto out_scsi_register;
-
/* initialize request counter */
BUG_ON(!zfcp_reqlist_isempty(adapter));
adapter->req_no = 0;
@@ -123,8 +119,6 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
flush_work(&adapter->scan_work);
return 0;
- out_scsi_register:
- zfcp_erp_thread_kill(adapter);
out:
up(&zfcp_data.config_sema);
return retval;
diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c
index f1a7518e67ed..10cbfd172a28 100644
--- a/drivers/s390/scsi/zfcp_cfdc.c
+++ b/drivers/s390/scsi/zfcp_cfdc.c
@@ -85,20 +85,9 @@ static int zfcp_cfdc_copy_to_user(void __user *user_buffer,
static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno)
{
- struct zfcp_adapter *adapter = NULL, *cur_adapter;
- struct ccw_dev_id dev_id;
-
- read_lock_irq(&zfcp_data.config_lock);
- list_for_each_entry(cur_adapter, &zfcp_data.adapter_list_head, list) {
- ccw_device_get_id(cur_adapter->ccw_device, &dev_id);
- if (dev_id.devno == devno) {
- adapter = cur_adapter;
- zfcp_adapter_get(adapter);
- break;
- }
- }
- read_unlock_irq(&zfcp_data.config_lock);
- return adapter;
+ char busid[9];
+ snprintf(busid, sizeof(busid), "0.0.%04x", devno);
+ return zfcp_get_adapter_by_busid(busid);
}
static int zfcp_cfdc_set_fsf(struct zfcp_fsf_cfdc *fsf_cfdc, int command)
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 735d675623f8..cb6df609953e 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -522,7 +522,7 @@ static const char *zfcp_rec_dbf_ids[] = {
[29] = "link down",
[30] = "link up status read",
[31] = "open port failed",
- [32] = "open port failed",
+ [32] = "",
[33] = "close port",
[34] = "open unit failed",
[35] = "exclusive open unit failed",
@@ -936,6 +936,7 @@ void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req)
rct->reason_code = hdr->reason_code;
rct->expl = hdr->reason_code_expl;
rct->vendor_unique = hdr->vendor_unique;
+ rct->max_res_size = hdr->max_res_size;
rct->len = min((int)ct->resp->length - (int)sizeof(struct ct_hdr),
ZFCP_DBF_SAN_MAX_PAYLOAD);
debug_event(adapter->san_dbf, level, r, sizeof(*r));
@@ -1043,6 +1044,7 @@ static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view,
zfcp_dbf_out(&p, "reason_code", "0x%02x", ct->reason_code);
zfcp_dbf_out(&p, "reason_code_expl", "0x%02x", ct->expl);
zfcp_dbf_out(&p, "vendor_unique", "0x%02x", ct->vendor_unique);
+ zfcp_dbf_out(&p, "max_res_size", "0x%04x", ct->max_res_size);
} else if (strncmp(r->tag, "oels", ZFCP_DBF_TAG_SIZE) == 0 ||
strncmp(r->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 ||
strncmp(r->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) {
@@ -1249,7 +1251,7 @@ int zfcp_adapter_debug_register(struct zfcp_adapter *adapter)
char dbf_name[DEBUG_MAX_NAME_LEN];
/* debug feature area which records recovery activity */
- sprintf(dbf_name, "zfcp_%s_rec", zfcp_get_busid_by_adapter(adapter));
+ sprintf(dbf_name, "zfcp_%s_rec", dev_name(&adapter->ccw_device->dev));
adapter->rec_dbf = debug_register(dbf_name, dbfsize, 1,
sizeof(struct zfcp_rec_dbf_record));
if (!adapter->rec_dbf)
@@ -1259,7 +1261,7 @@ int zfcp_adapter_debug_register(struct zfcp_adapter *adapter)
debug_set_level(adapter->rec_dbf, 3);
/* debug feature area which records HBA (FSF and QDIO) conditions */
- sprintf(dbf_name, "zfcp_%s_hba", zfcp_get_busid_by_adapter(adapter));
+ sprintf(dbf_name, "zfcp_%s_hba", dev_name(&adapter->ccw_device->dev));
adapter->hba_dbf = debug_register(dbf_name, dbfsize, 1,
sizeof(struct zfcp_hba_dbf_record));
if (!adapter->hba_dbf)
@@ -1269,7 +1271,7 @@ int zfcp_adapter_debug_register(struct zfcp_adapter *adapter)
debug_set_level(adapter->hba_dbf, 3);
/* debug feature area which records SAN command failures and recovery */
- sprintf(dbf_name, "zfcp_%s_san", zfcp_get_busid_by_adapter(adapter));
+ sprintf(dbf_name, "zfcp_%s_san", dev_name(&adapter->ccw_device->dev));
adapter->san_dbf = debug_register(dbf_name, dbfsize, 1,
sizeof(struct zfcp_san_dbf_record));
if (!adapter->san_dbf)
@@ -1279,7 +1281,7 @@ int zfcp_adapter_debug_register(struct zfcp_adapter *adapter)
debug_set_level(adapter->san_dbf, 6);
/* debug feature area which records SCSI command failures and recovery */
- sprintf(dbf_name, "zfcp_%s_scsi", zfcp_get_busid_by_adapter(adapter));
+ sprintf(dbf_name, "zfcp_%s_scsi", dev_name(&adapter->ccw_device->dev));
adapter->scsi_dbf = debug_register(dbf_name, dbfsize, 1,
sizeof(struct zfcp_scsi_dbf_record));
if (!adapter->scsi_dbf)
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 5d6b2dff855b..74998ff88e57 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -171,6 +171,7 @@ struct zfcp_san_dbf_record_ct_response {
u8 reason_code;
u8 expl;
u8 vendor_unique;
+ u16 max_res_size;
u32 len;
} __attribute__ ((packed));
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index e19e46ae4a68..510662783a6f 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -159,20 +159,6 @@ struct fcp_rscn_element {
u32 nport_did:24;
} __attribute__((packed));
-#define ZFCP_PORT_ADDRESS 0x0
-#define ZFCP_AREA_ADDRESS 0x1
-#define ZFCP_DOMAIN_ADDRESS 0x2
-#define ZFCP_FABRIC_ADDRESS 0x3
-
-#define ZFCP_PORTS_RANGE_PORT 0xFFFFFF
-#define ZFCP_PORTS_RANGE_AREA 0xFFFF00
-#define ZFCP_PORTS_RANGE_DOMAIN 0xFF0000
-#define ZFCP_PORTS_RANGE_FABRIC 0x000000
-
-#define ZFCP_NO_PORTS_PER_AREA 0x100
-#define ZFCP_NO_PORTS_PER_DOMAIN 0x10000
-#define ZFCP_NO_PORTS_PER_FABRIC 0x1000000
-
/* see fc-ph */
struct fcp_logo {
u32 command;
@@ -211,7 +197,6 @@ struct zfcp_ls_adisc {
#define ZFCP_CT_UNABLE_TO_PERFORM_CMD 0x09
#define ZFCP_CT_GID_PN 0x0121
#define ZFCP_CT_GPN_FT 0x0172
-#define ZFCP_CT_MAX_SIZE 0x1020
#define ZFCP_CT_ACCEPT 0x8002
#define ZFCP_CT_REJECT 0x8001
@@ -258,7 +243,6 @@ struct zfcp_ls_adisc {
/* remote port status */
#define ZFCP_STATUS_PORT_PHYS_OPEN 0x00000001
-#define ZFCP_STATUS_PORT_DID_DID 0x00000002
#define ZFCP_STATUS_PORT_PHYS_CLOSING 0x00000004
#define ZFCP_STATUS_PORT_NO_WWPN 0x00000008
#define ZFCP_STATUS_PORT_INVALID_WWPN 0x00000020
@@ -340,8 +324,6 @@ struct ct_iu_gid_pn_resp {
* @wka_port: port where the request is sent to
* @req: scatter-gather list for request
* @resp: scatter-gather list for response
- * @req_count: number of elements in request scatter-gather list
- * @resp_count: number of elements in response scatter-gather list
* @handler: handler function (called for response to the request)
* @handler_data: data passed to handler function
* @timeout: FSF timeout for this request
@@ -352,8 +334,6 @@ struct zfcp_send_ct {
struct zfcp_wka_port *wka_port;
struct scatterlist *req;
struct scatterlist *resp;
- unsigned int req_count;
- unsigned int resp_count;
void (*handler)(unsigned long);
unsigned long handler_data;
int timeout;
@@ -378,8 +358,6 @@ struct zfcp_gid_pn_data {
* @d_id: destiniation id of port where request is sent to
* @req: scatter-gather list for request
* @resp: scatter-gather list for response
- * @req_count: number of elements in request scatter-gather list
- * @resp_count: number of elements in response scatter-gather list
* @handler: handler function (called for response to the request)
* @handler_data: data passed to handler function
* @completion: completion for synchronization purposes
@@ -392,8 +370,6 @@ struct zfcp_send_els {
u32 d_id;
struct scatterlist *req;
struct scatterlist *resp;
- unsigned int req_count;
- unsigned int resp_count;
void (*handler)(unsigned long);
unsigned long handler_data;
struct completion *completion;
@@ -451,7 +427,6 @@ struct zfcp_latencies {
};
struct zfcp_adapter {
- struct list_head list; /* list of adapters */
atomic_t refcount; /* reference count */
wait_queue_head_t remove_wq; /* can be used to wait for
refcount drop to zero */
@@ -593,16 +568,11 @@ struct zfcp_fsf_req {
struct zfcp_data {
struct scsi_host_template scsi_host_template;
struct scsi_transport_template *scsi_transport_template;
- struct list_head adapter_list_head; /* head of adapter list */
rwlock_t config_lock; /* serialises changes
to adapter/port/unit
lists */
struct semaphore config_sema; /* serialises configuration
changes */
- atomic_t loglevel; /* current loglevel */
- char init_busid[20];
- u64 init_wwpn;
- u64 init_fcp_lun;
struct kmem_cache *fsf_req_qtcb_cache;
struct kmem_cache *sr_buffer_cache;
struct kmem_cache *gid_pn_cache;
@@ -623,8 +593,6 @@ struct zfcp_fsf_req_qtcb {
#define ZFCP_SET 0x00000100
#define ZFCP_CLEAR 0x00000200
-#define zfcp_get_busid_by_adapter(adapter) (dev_name(&adapter->ccw_device->dev))
-
/*
* Helper functions for request ID management.
*/
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 4ed4950d994b..387a3af528ac 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -840,7 +840,6 @@ static int zfcp_erp_open_ptp_port(struct zfcp_erp_action *act)
return ZFCP_ERP_FAILED;
}
port->d_id = adapter->peer_d_id;
- atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
return zfcp_erp_port_strategy_open_port(act);
}
@@ -871,12 +870,12 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
case ZFCP_ERP_STEP_PORT_CLOSING:
if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP)
return zfcp_erp_open_ptp_port(act);
- if (!(p_status & ZFCP_STATUS_PORT_DID_DID)) {
+ if (!port->d_id) {
queue_work(zfcp_data.work_queue, &port->gid_pn_work);
return ZFCP_ERP_CONTINUES;
}
case ZFCP_ERP_STEP_NAMESERVER_LOOKUP:
- if (!(p_status & ZFCP_STATUS_PORT_DID_DID)) {
+ if (!port->d_id) {
if (p_status & (ZFCP_STATUS_PORT_INVALID_WWPN)) {
zfcp_erp_port_failed(port, 26, NULL);
return ZFCP_ERP_EXIT;
@@ -888,7 +887,7 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
case ZFCP_ERP_STEP_PORT_OPENING:
/* D_ID might have changed during open */
if (p_status & ZFCP_STATUS_COMMON_OPEN) {
- if (p_status & ZFCP_STATUS_PORT_DID_DID)
+ if (port->d_id)
return ZFCP_ERP_SUCCEEDED;
else {
act->step = ZFCP_ERP_STEP_PORT_CLOSING;
@@ -1385,6 +1384,7 @@ static int zfcp_erp_thread(void *data)
struct list_head *next;
struct zfcp_erp_action *act;
unsigned long flags;
+ int ignore;
daemonize("zfcperp%s", dev_name(&adapter->ccw_device->dev));
/* Block all signals */
@@ -1407,7 +1407,7 @@ static int zfcp_erp_thread(void *data)
}
zfcp_rec_dbf_event_thread_lock(4, adapter);
- down_interruptible(&adapter->erp_ready_sem);
+ ignore = down_interruptible(&adapter->erp_ready_sem);
zfcp_rec_dbf_event_thread_lock(5, adapter);
}
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index f009f2a7ec3e..eabdfe24456e 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -11,6 +11,20 @@
#include "zfcp_ext.h"
+enum rscn_address_format {
+ RSCN_PORT_ADDRESS = 0x0,
+ RSCN_AREA_ADDRESS = 0x1,
+ RSCN_DOMAIN_ADDRESS = 0x2,
+ RSCN_FABRIC_ADDRESS = 0x3,
+};
+
+static u32 rscn_range_mask[] = {
+ [RSCN_PORT_ADDRESS] = 0xFFFFFF,
+ [RSCN_AREA_ADDRESS] = 0xFFFF00,
+ [RSCN_DOMAIN_ADDRESS] = 0xFF0000,
+ [RSCN_FABRIC_ADDRESS] = 0x000000,
+};
+
struct ct_iu_gpn_ft_req {
struct ct_hdr header;
u8 flags;
@@ -26,9 +40,12 @@ struct gpn_ft_resp_acc {
u64 wwpn;
} __attribute__ ((packed));
-#define ZFCP_GPN_FT_ENTRIES ((PAGE_SIZE - sizeof(struct ct_hdr)) \
- / sizeof(struct gpn_ft_resp_acc))
+#define ZFCP_CT_SIZE_ONE_PAGE (PAGE_SIZE - sizeof(struct ct_hdr))
+#define ZFCP_GPN_FT_ENTRIES (ZFCP_CT_SIZE_ONE_PAGE \
+ / sizeof(struct gpn_ft_resp_acc))
#define ZFCP_GPN_FT_BUFFERS 4
+#define ZFCP_GPN_FT_MAX_SIZE (ZFCP_GPN_FT_BUFFERS * PAGE_SIZE \
+ - sizeof(struct ct_hdr))
#define ZFCP_GPN_FT_MAX_ENTRIES ZFCP_GPN_FT_BUFFERS * (ZFCP_GPN_FT_ENTRIES + 1)
struct ct_iu_gpn_ft_resp {
@@ -160,22 +177,7 @@ static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req)
for (i = 1; i < no_entries; i++) {
/* skip head and start with 1st element */
fcp_rscn_element++;
- switch (fcp_rscn_element->addr_format) {
- case ZFCP_PORT_ADDRESS:
- range_mask = ZFCP_PORTS_RANGE_PORT;
- break;
- case ZFCP_AREA_ADDRESS:
- range_mask = ZFCP_PORTS_RANGE_AREA;
- break;
- case ZFCP_DOMAIN_ADDRESS:
- range_mask = ZFCP_PORTS_RANGE_DOMAIN;
- break;
- case ZFCP_FABRIC_ADDRESS:
- range_mask = ZFCP_PORTS_RANGE_FABRIC;
- break;
- default:
- continue;
- }
+ range_mask = rscn_range_mask[fcp_rscn_element->addr_format];
_zfcp_fc_incoming_rscn(fsf_req, range_mask, fcp_rscn_element);
}
schedule_work(&fsf_req->adapter->scan_work);
@@ -266,7 +268,6 @@ static void zfcp_fc_ns_gid_pn_eval(unsigned long data)
return;
/* looks like a valid d_id */
port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK;
- atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
}
int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action,
@@ -284,8 +285,6 @@ int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action,
gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
gid_pn->ct.req = &gid_pn->req;
gid_pn->ct.resp = &gid_pn->resp;
- gid_pn->ct.req_count = 1;
- gid_pn->ct.resp_count = 1;
sg_init_one(&gid_pn->req, &gid_pn->ct_iu_req,
sizeof(struct ct_iu_gid_pn_req));
sg_init_one(&gid_pn->resp, &gid_pn->ct_iu_resp,
@@ -297,7 +296,7 @@ int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action,
gid_pn->ct_iu_req.header.gs_subtype = ZFCP_CT_NAME_SERVER;
gid_pn->ct_iu_req.header.options = ZFCP_CT_SYNCHRONOUS;
gid_pn->ct_iu_req.header.cmd_rsp_code = ZFCP_CT_GID_PN;
- gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_MAX_SIZE;
+ gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_SIZE_ONE_PAGE / 4;
gid_pn->ct_iu_req.wwpn = erp_action->port->wwpn;
init_completion(&compl_rec.done);
@@ -407,8 +406,6 @@ static int zfcp_fc_adisc(struct zfcp_port *port)
sg_init_one(adisc->els.resp, &adisc->ls_adisc_acc,
sizeof(struct zfcp_ls_adisc));
- adisc->els.req_count = 1;
- adisc->els.resp_count = 1;
adisc->els.adapter = adapter;
adisc->els.port = port;
adisc->els.d_id = port->d_id;
@@ -448,17 +445,17 @@ void zfcp_test_link(struct zfcp_port *port)
zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
}
-static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft)
+static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft, int buf_num)
{
struct scatterlist *sg = &gpn_ft->sg_req;
kfree(sg_virt(sg)); /* free request buffer */
- zfcp_sg_free_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS);
+ zfcp_sg_free_table(gpn_ft->sg_resp, buf_num);
kfree(gpn_ft);
}
-static struct zfcp_gpn_ft *zfcp_alloc_sg_env(void)
+static struct zfcp_gpn_ft *zfcp_alloc_sg_env(int buf_num)
{
struct zfcp_gpn_ft *gpn_ft;
struct ct_iu_gpn_ft_req *req;
@@ -475,8 +472,8 @@ static struct zfcp_gpn_ft *zfcp_alloc_sg_env(void)
}
sg_init_one(&gpn_ft->sg_req, req, sizeof(*req));
- if (zfcp_sg_setup_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS)) {
- zfcp_free_sg_env(gpn_ft);
+ if (zfcp_sg_setup_table(gpn_ft->sg_resp, buf_num)) {
+ zfcp_free_sg_env(gpn_ft, buf_num);
gpn_ft = NULL;
}
out:
@@ -485,7 +482,8 @@ out:
static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
- struct zfcp_adapter *adapter)
+ struct zfcp_adapter *adapter,
+ int max_bytes)
{
struct zfcp_send_ct *ct = &gpn_ft->ct;
struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
@@ -498,8 +496,7 @@ static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
req->header.options = ZFCP_CT_SYNCHRONOUS;
req->header.cmd_rsp_code = ZFCP_CT_GPN_FT;
- req->header.max_res_size = (sizeof(struct gpn_ft_resp_acc) *
- (ZFCP_GPN_FT_MAX_ENTRIES - 1)) >> 2;
+ req->header.max_res_size = max_bytes / 4;
req->flags = 0;
req->domain_id_scope = 0;
req->area_id_scope = 0;
@@ -512,8 +509,6 @@ static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
ct->timeout = 10;
ct->req = &gpn_ft->sg_req;
ct->resp = gpn_ft->sg_resp;
- ct->req_count = 1;
- ct->resp_count = ZFCP_GPN_FT_BUFFERS;
init_completion(&compl_rec.done);
compl_rec.handler = NULL;
@@ -540,7 +535,7 @@ static void zfcp_validate_port(struct zfcp_port *port)
zfcp_port_dequeue(port);
}
-static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
+static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries)
{
struct zfcp_send_ct *ct = &gpn_ft->ct;
struct scatterlist *sg = gpn_ft->sg_resp;
@@ -560,13 +555,17 @@ static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
return -EIO;
}
- if (hdr->max_res_size)
+ if (hdr->max_res_size) {
+ dev_warn(&adapter->ccw_device->dev,
+ "The name server reported %d words residual data\n",
+ hdr->max_res_size);
return -E2BIG;
+ }
down(&zfcp_data.config_sema);
/* first entry is the header */
- for (x = 1; x < ZFCP_GPN_FT_MAX_ENTRIES && !last; x++) {
+ for (x = 1; x < max_entries && !last; x++) {
if (x % (ZFCP_GPN_FT_ENTRIES + 1))
acc++;
else
@@ -589,7 +588,6 @@ static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
}
port = zfcp_port_enqueue(adapter, acc->wwpn,
- ZFCP_STATUS_PORT_DID_DID |
ZFCP_STATUS_COMMON_NOESC, d_id);
if (IS_ERR(port))
ret = PTR_ERR(port);
@@ -612,6 +610,12 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter)
{
int ret, i;
struct zfcp_gpn_ft *gpn_ft;
+ int chain, max_entries, buf_num, max_bytes;
+
+ chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS;
+ buf_num = chain ? ZFCP_GPN_FT_BUFFERS : 1;
+ max_entries = chain ? ZFCP_GPN_FT_MAX_ENTRIES : ZFCP_GPN_FT_ENTRIES;
+ max_bytes = chain ? ZFCP_GPN_FT_MAX_SIZE : ZFCP_CT_SIZE_ONE_PAGE;
if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT)
return 0;
@@ -620,23 +624,23 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter)
if (ret)
return ret;
- gpn_ft = zfcp_alloc_sg_env();
+ gpn_ft = zfcp_alloc_sg_env(buf_num);
if (!gpn_ft) {
ret = -ENOMEM;
goto out;
}
for (i = 0; i < 3; i++) {
- ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter);
+ ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter, max_bytes);
if (!ret) {
- ret = zfcp_scan_eval_gpn_ft(gpn_ft);
+ ret = zfcp_scan_eval_gpn_ft(gpn_ft, max_entries);
if (ret == -EAGAIN)
ssleep(1);
else
break;
}
}
- zfcp_free_sg_env(gpn_ft);
+ zfcp_free_sg_env(gpn_ft, buf_num);
out:
zfcp_wka_port_put(&adapter->nsp);
return ret;
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 9c72e083559d..e6416f8541b0 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -644,38 +644,38 @@ static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req)
}
}
-static int zfcp_fsf_sbal_check(struct zfcp_adapter *adapter)
+static int zfcp_fsf_sbal_available(struct zfcp_adapter *adapter)
{
- struct zfcp_qdio_queue *req_q = &adapter->req_q;
-
- spin_lock_bh(&adapter->req_q_lock);
- if (atomic_read(&req_q->count))
+ if (atomic_read(&adapter->req_q.count) > 0)
return 1;
- spin_unlock_bh(&adapter->req_q_lock);
+ atomic_inc(&adapter->qdio_outb_full);
return 0;
}
-static int zfcp_fsf_sbal_available(struct zfcp_adapter *adapter)
-{
- unsigned int count = atomic_read(&adapter->req_q.count);
- if (!count)
- atomic_inc(&adapter->qdio_outb_full);
- return count > 0;
-}
-
static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter)
+ __releases(&adapter->req_q_lock)
+ __acquires(&adapter->req_q_lock)
{
+ struct zfcp_qdio_queue *req_q = &adapter->req_q;
long ret;
+ if (atomic_read(&req_q->count) <= -REQUEST_LIST_SIZE)
+ return -EIO;
+ if (atomic_read(&req_q->count) > 0)
+ return 0;
+
+ atomic_dec(&req_q->count);
spin_unlock_bh(&adapter->req_q_lock);
ret = wait_event_interruptible_timeout(adapter->request_wq,
- zfcp_fsf_sbal_check(adapter), 5 * HZ);
+ atomic_read(&req_q->count) >= 0,
+ 5 * HZ);
+ spin_lock_bh(&adapter->req_q_lock);
+ atomic_inc(&req_q->count);
+
if (ret > 0)
return 0;
if (!ret)
atomic_inc(&adapter->qdio_outb_full);
-
- spin_lock_bh(&adapter->req_q_lock);
return -EIO;
}
@@ -1013,12 +1013,29 @@ skip_fsfstatus:
send_ct->handler(send_ct->handler_data);
}
-static int zfcp_fsf_setup_sbals(struct zfcp_fsf_req *req,
- struct scatterlist *sg_req,
- struct scatterlist *sg_resp, int max_sbals)
+static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
+ struct scatterlist *sg_req,
+ struct scatterlist *sg_resp,
+ int max_sbals)
{
+ struct qdio_buffer_element *sbale = zfcp_qdio_sbale_req(req);
+ u32 feat = req->adapter->adapter_features;
int bytes;
+ if (!(feat & FSF_FEATURE_ELS_CT_CHAINED_SBALS)) {
+ if (sg_req->length > PAGE_SIZE || sg_resp->length > PAGE_SIZE ||
+ !sg_is_last(sg_req) || !sg_is_last(sg_resp))
+ return -EOPNOTSUPP;
+
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
+ sbale[2].addr = sg_virt(sg_req);
+ sbale[2].length = sg_req->length;
+ sbale[3].addr = sg_virt(sg_resp);
+ sbale[3].length = sg_resp->length;
+ sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
+ return 0;
+ }
+
bytes = zfcp_qdio_sbals_from_sg(req, SBAL_FLAGS0_TYPE_WRITE_READ,
sg_req, max_sbals);
if (bytes <= 0)
@@ -1060,8 +1077,8 @@ int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
goto out;
}
- ret = zfcp_fsf_setup_sbals(req, ct->req, ct->resp,
- FSF_MAX_SBALS_PER_REQ);
+ ret = zfcp_fsf_setup_ct_els_sbals(req, ct->req, ct->resp,
+ FSF_MAX_SBALS_PER_REQ);
if (ret)
goto failed_send;
@@ -1171,7 +1188,7 @@ int zfcp_fsf_send_els(struct zfcp_send_els *els)
goto out;
}
- ret = zfcp_fsf_setup_sbals(req, els->req, els->resp, 2);
+ ret = zfcp_fsf_setup_ct_els_sbals(req, els->req, els->resp, 2);
if (ret)
goto failed_send;
@@ -1406,13 +1423,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
switch (header->fsf_status_qual.word[0]) {
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
- req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
case FSF_SQ_NO_RETRY_POSSIBLE:
- dev_warn(&req->adapter->ccw_device->dev,
- "Remote port 0x%016Lx could not be opened\n",
- (unsigned long long)port->wwpn);
- zfcp_erp_port_failed(port, 32, req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
@@ -1440,10 +1451,10 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
* Alternately, an ADISC/PDISC ELS should suffice, as well.
*/
plogi = (struct fsf_plogi *) req->qtcb->bottom.support.els;
- if (req->qtcb->bottom.support.els1_length >= sizeof(*plogi)) {
+ if (req->qtcb->bottom.support.els1_length >=
+ FSF_PLOGI_MIN_LEN) {
if (plogi->serv_param.wwpn != port->wwpn)
- atomic_clear_mask(ZFCP_STATUS_PORT_DID_DID,
- &port->status);
+ port->d_id = 0;
else {
port->wwnn = plogi->serv_param.wwnn;
zfcp_fc_plogi_evaluate(port, plogi);
@@ -1907,7 +1918,7 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
dev_err(&adapter->ccw_device->dev,
"Shared read-write access not "
"supported (unit 0x%016Lx, port "
- "0x%016Lx\n)",
+ "0x%016Lx)\n",
(unsigned long long)unit->fcp_lun,
(unsigned long long)unit->port->wwpn);
zfcp_erp_unit_failed(unit, 36, req);
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index fa2a31780611..8bb200252347 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -164,6 +164,7 @@
#define FSF_FEATURE_LUN_SHARING 0x00000004
#define FSF_FEATURE_NOTIFICATION_LOST 0x00000008
#define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010
+#define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020
#define FSF_FEATURE_UPDATE_ALERT 0x00000100
#define FSF_FEATURE_MEASUREMENT_DATA 0x00000200
@@ -322,6 +323,7 @@ struct fsf_nport_serv_param {
u8 vendor_version_level[16];
} __attribute__ ((packed));
+#define FSF_PLOGI_MIN_LEN 112
struct fsf_plogi {
u32 code;
struct fsf_nport_serv_param serv_param;
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
index d3b55fb66f13..33e0a206a0a4 100644
--- a/drivers/s390/scsi/zfcp_qdio.c
+++ b/drivers/s390/scsi/zfcp_qdio.c
@@ -112,7 +112,7 @@ static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter,
* corruption and must stop the machine immediatly.
*/
panic("error: unknown request id (%lx) on adapter %s.\n",
- req_id, zfcp_get_busid_by_adapter(adapter));
+ req_id, dev_name(&adapter->ccw_device->dev));
zfcp_reqlist_remove(adapter, fsf_req);
spin_unlock_irqrestore(&adapter->req_list_lock, flags);
@@ -392,7 +392,7 @@ int zfcp_qdio_allocate(struct zfcp_adapter *adapter)
init_data->cdev = adapter->ccw_device;
init_data->q_format = QDIO_ZFCP_QFMT;
- memcpy(init_data->adapter_name, zfcp_get_busid_by_adapter(adapter), 8);
+ memcpy(init_data->adapter_name, dev_name(&adapter->ccw_device->dev), 8);
ASCEBC(init_data->adapter_name, 8);
init_data->qib_param_field_format = 0;
init_data->qib_param_field = NULL;
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 403ecad48d4b..152d4aa9354f 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -352,6 +352,8 @@ config ISCSI_TCP
http://open-iscsi.org
+source "drivers/scsi/cxgb3i/Kconfig"
+
config SGIWD93_SCSI
tristate "SGI WD93C93 SCSI Driver"
depends on SGI_HAS_WD93 && SCSI
@@ -603,6 +605,19 @@ config SCSI_FLASHPOINT
substantial, so users of MultiMaster Host Adapters may not
wish to include it.
+config LIBFC
+ tristate "LibFC module"
+ select SCSI_FC_ATTRS
+ ---help---
+ Fibre Channel library module
+
+config FCOE
+ tristate "FCoE module"
+ depends on PCI
+ select LIBFC
+ ---help---
+ Fibre Channel over Ethernet module
+
config SCSI_DMX3191D
tristate "DMX3191D SCSI support"
depends on PCI && SCSI
@@ -1357,6 +1372,13 @@ config SCSI_LPFC
This lpfc driver supports the Emulex LightPulse
Family of Fibre Channel PCI host adapters.
+config SCSI_LPFC_DEBUG_FS
+ bool "Emulex LightPulse Fibre Channel debugfs Support"
+ depends on SCSI_LPFC && DEBUG_FS
+ help
+ This makes debugging infomation from the lpfc driver
+ available via the debugfs filesystem.
+
config SCSI_SIM710
tristate "Simple 53c710 SCSI support (Compaq, NCR machines)"
depends on (EISA || MCA) && SCSI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 72fd5043cfa1..1410697257cb 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -36,7 +36,9 @@ obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas/
obj-$(CONFIG_SCSI_SRP_ATTRS) += scsi_transport_srp.o
obj-$(CONFIG_SCSI_DH) += device_handler/
-obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o
+obj-$(CONFIG_LIBFC) += libfc/
+obj-$(CONFIG_FCOE) += fcoe/
+obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
obj-$(CONFIG_SCSI_A4000T) += 53c700.o a4000t.o
obj-$(CONFIG_SCSI_ZORRO7XX) += 53c700.o zorro7xx.o
@@ -124,6 +126,7 @@ obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o
obj-$(CONFIG_SCSI_STEX) += stex.o
obj-$(CONFIG_SCSI_MVSAS) += mvsas.o
obj-$(CONFIG_PS3_ROM) += ps3rom.o
+obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libiscsi.o libiscsi_tcp.o cxgb3i/
obj-$(CONFIG_ARM) += arm/
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
index eeddbd19eba5..f92da9fd5f20 100644
--- a/drivers/scsi/NCR5380.c
+++ b/drivers/scsi/NCR5380.c
@@ -30,7 +30,7 @@
* $Log: NCR5380.c,v $
* Revision 1.10 1998/9/2 Alan Cox
- * (alan@redhat.com)
+ * (alan@lxorguk.ukuu.org.uk)
* Fixed up the timer lockups reported so far. Things still suck. Looking
* forward to 2.3 and per device request queues. Then it'll be possible to
* SMP thread this beast and improve life no end.
diff --git a/drivers/scsi/a100u2w.c b/drivers/scsi/a100u2w.c
index 84bb61628372..3c298c7253ee 100644
--- a/drivers/scsi/a100u2w.c
+++ b/drivers/scsi/a100u2w.c
@@ -54,7 +54,7 @@
* 9/28/04 Christoph Hellwig <hch@lst.de>
* - merge the two source files
* - remove internal queueing code
- * 14/06/07 Alan Cox <alan@redhat.com>
+ * 14/06/07 Alan Cox <alan@lxorguk.ukuu.org.uk>
* - Grand cleanup and Linuxisation
*/
diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c
index 8abfd06b5a72..90d1d0878cb8 100644
--- a/drivers/scsi/aacraid/aachba.c
+++ b/drivers/scsi/aacraid/aachba.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c
index a7355260cfcf..0391d759dfdb 100644
--- a/drivers/scsi/aacraid/commctrl.c
+++ b/drivers/scsi/aacraid/commctrl.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
@@ -90,14 +90,24 @@ static int ioctl_send_fib(struct aac_dev * dev, void __user *arg)
if (size < le16_to_cpu(kfib->header.SenderSize))
size = le16_to_cpu(kfib->header.SenderSize);
if (size > dev->max_fib_size) {
+ dma_addr_t daddr;
+
if (size > 2048) {
retval = -EINVAL;
goto cleanup;
}
+
+ kfib = pci_alloc_consistent(dev->pdev, size, &daddr);
+ if (!kfib) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+
/* Highjack the hw_fib */
hw_fib = fibptr->hw_fib_va;
hw_fib_pa = fibptr->hw_fib_pa;
- fibptr->hw_fib_va = kfib = pci_alloc_consistent(dev->pdev, size, &fibptr->hw_fib_pa);
+ fibptr->hw_fib_va = kfib;
+ fibptr->hw_fib_pa = daddr;
memset(((char *)kfib) + dev->max_fib_size, 0, size - dev->max_fib_size);
memcpy(kfib, hw_fib, dev->max_fib_size);
}
diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
index cbac06355107..16310443b55a 100644
--- a/drivers/scsi/aacraid/comminit.c
+++ b/drivers/scsi/aacraid/comminit.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index 289304aab690..d24c2670040b 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/dpcsup.c b/drivers/scsi/aacraid/dpcsup.c
index 933f208eedba..abc9ef5d1b10 100644
--- a/drivers/scsi/aacraid/dpcsup.c
+++ b/drivers/scsi/aacraid/dpcsup.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 94acbeed4e7c..36d8aab97efe 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/rkt.c b/drivers/scsi/aacraid/rkt.c
index 8cd6588a83e3..16d8db550027 100644
--- a/drivers/scsi/aacraid/rkt.c
+++ b/drivers/scsi/aacraid/rkt.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c
index 073208b0f622..f70d9f8e79e5 100644
--- a/drivers/scsi/aacraid/rx.c
+++ b/drivers/scsi/aacraid/rx.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/sa.c b/drivers/scsi/aacraid/sa.c
index fc1a55796a89..b6a3c5c187b6 100644
--- a/drivers/scsi/aacraid/sa.c
+++ b/drivers/scsi/aacraid/sa.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
index 399fe559e4de..2f602720193e 100644
--- a/drivers/scsi/advansys.c
+++ b/drivers/scsi/advansys.c
@@ -13425,8 +13425,7 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
}
boardp->asc_n_io_port = pci_resource_len(pdev, 1);
- boardp->ioremap_addr = ioremap(pci_resource_start(pdev, 1),
- boardp->asc_n_io_port);
+ boardp->ioremap_addr = pci_ioremap_bar(pdev, 1);
if (!boardp->ioremap_addr) {
shost_printk(KERN_ERR, shost, "ioremap(%lx, %d) "
"returned NULL\n",
diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c
index 7c45d88a205b..ed0e3e55652a 100644
--- a/drivers/scsi/aha1740.c
+++ b/drivers/scsi/aha1740.c
@@ -22,7 +22,7 @@
* aha1740_makecode may still need even more work
* if it doesn't work for your devices, take a look.
*
- * Reworked for new_eh and new locking by Alan Cox <alan@redhat.com>
+ * Reworked for new_eh and new locking by Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* Converted to EISA and generic DMA APIs by Marc Zyngier
* <maz@wild-wind.fr.eu.org>, 4/2003.
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index f91f79c8007d..106c04d2d793 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -235,7 +235,7 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
uint32_t intmask_org;
int i, j;
- acb->pmuA = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+ acb->pmuA = pci_ioremap_bar(pdev, 0);
if (!acb->pmuA) {
printk(KERN_NOTICE "arcmsr%d: memory mapping region fail \n",
acb->host->host_no);
@@ -329,13 +329,11 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
reg = (struct MessageUnit_B *)(dma_coherent +
ARCMSR_MAX_FREECCB_NUM * sizeof(struct CommandControlBlock));
acb->pmuB = reg;
- mem_base0 = ioremap(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
+ mem_base0 = pci_ioremap_bar(pdev, 0);
if (!mem_base0)
goto out;
- mem_base1 = ioremap(pci_resource_start(pdev, 2),
- pci_resource_len(pdev, 2));
+ mem_base1 = pci_ioremap_bar(pdev, 2);
if (!mem_base1) {
iounmap(mem_base0);
goto out;
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c
index 7d311541c76c..20ca0a6374b5 100644
--- a/drivers/scsi/atp870u.c
+++ b/drivers/scsi/atp870u.c
@@ -1,8 +1,8 @@
/*
* Copyright (C) 1997 Wu Ching Chen
* 2.1.x update (C) 1998 Krzysztof G. Baranowski
- * 2.5.x update (C) 2002 Red Hat <alan@redhat.com>
- * 2.6.x update (C) 2004 Red Hat <alan@redhat.com>
+ * 2.5.x update (C) 2002 Red Hat
+ * 2.6.x update (C) 2004 Red Hat
*
* Marcelo Tosatti <marcelo@conectiva.com.br> : SMP fixes
*
diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c
index 88ecf94ad979..af9725409f43 100644
--- a/drivers/scsi/ch.c
+++ b/drivers/scsi/ch.c
@@ -190,7 +190,7 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
result = scsi_execute_req(ch->device, cmd, direction, buffer,
buflength, &sshdr, timeout * HZ,
- MAX_RETRIES);
+ MAX_RETRIES, NULL);
dprintk("result: 0x%x\n",result);
if (driver_byte(result) & DRIVER_SENSE) {
diff --git a/drivers/scsi/cxgb3i/Kbuild b/drivers/scsi/cxgb3i/Kbuild
new file mode 100644
index 000000000000..ee7d6d2f9c3b
--- /dev/null
+++ b/drivers/scsi/cxgb3i/Kbuild
@@ -0,0 +1,4 @@
+EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/cxgb3
+
+cxgb3i-y := cxgb3i_init.o cxgb3i_iscsi.o cxgb3i_pdu.o cxgb3i_offload.o
+obj-$(CONFIG_SCSI_CXGB3_ISCSI) += cxgb3i_ddp.o cxgb3i.o
diff --git a/drivers/scsi/cxgb3i/Kconfig b/drivers/scsi/cxgb3i/Kconfig
new file mode 100644
index 000000000000..bfdcaf5c9c57
--- /dev/null
+++ b/drivers/scsi/cxgb3i/Kconfig
@@ -0,0 +1,7 @@
+config SCSI_CXGB3_ISCSI
+ tristate "Chelsio S3xx iSCSI support"
+ depends on CHELSIO_T3_DEPENDS
+ select CHELSIO_T3
+ select SCSI_ISCSI_ATTRS
+ ---help---
+ This driver supports iSCSI offload for the Chelsio S3 series devices.
diff --git a/drivers/scsi/cxgb3i/cxgb3i.h b/drivers/scsi/cxgb3i/cxgb3i.h
new file mode 100644
index 000000000000..fde6e4c634e7
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i.h
@@ -0,0 +1,139 @@
+/*
+ * cxgb3i.h: Chelsio S3xx iSCSI driver.
+ *
+ * Copyright (c) 2008 Chelsio Communications, 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.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#ifndef __CXGB3I_H__
+#define __CXGB3I_H__
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/scatterlist.h>
+#include <scsi/libiscsi_tcp.h>
+
+/* from cxgb3 LLD */
+#include "common.h"
+#include "t3_cpl.h"
+#include "t3cdev.h"
+#include "cxgb3_ctl_defs.h"
+#include "cxgb3_offload.h"
+#include "firmware_exports.h"
+
+#include "cxgb3i_offload.h"
+#include "cxgb3i_ddp.h"
+
+#define CXGB3I_SCSI_QDEPTH_DFLT 128
+#define CXGB3I_MAX_TARGET CXGB3I_MAX_CONN
+#define CXGB3I_MAX_LUN 512
+#define ISCSI_PDU_NONPAYLOAD_MAX \
+ (sizeof(struct iscsi_hdr) + ISCSI_MAX_AHS_SIZE + 2*ISCSI_DIGEST_SIZE)
+
+struct cxgb3i_adapter;
+struct cxgb3i_hba;
+struct cxgb3i_endpoint;
+
+/**
+ * struct cxgb3i_hba - cxgb3i iscsi structure (per port)
+ *
+ * @snic: cxgb3i adapter containing this port
+ * @ndev: pointer to netdev structure
+ * @shost: pointer to scsi host structure
+ */
+struct cxgb3i_hba {
+ struct cxgb3i_adapter *snic;
+ struct net_device *ndev;
+ struct Scsi_Host *shost;
+};
+
+/**
+ * struct cxgb3i_adapter - cxgb3i adapter structure (per pci)
+ *
+ * @listhead: list head to link elements
+ * @lock: lock for this structure
+ * @tdev: pointer to t3cdev used by cxgb3 driver
+ * @pdev: pointer to pci dev
+ * @hba_cnt: # of hbas (the same as # of ports)
+ * @hba: all the hbas on this adapter
+ * @tx_max_size: max. tx packet size supported
+ * @rx_max_size: max. rx packet size supported
+ * @tag_format: ddp tag format settings
+ */
+struct cxgb3i_adapter {
+ struct list_head list_head;
+ spinlock_t lock;
+ struct t3cdev *tdev;
+ struct pci_dev *pdev;
+ unsigned char hba_cnt;
+ struct cxgb3i_hba *hba[MAX_NPORTS];
+
+ unsigned int tx_max_size;
+ unsigned int rx_max_size;
+
+ struct cxgb3i_tag_format tag_format;
+};
+
+/**
+ * struct cxgb3i_conn - cxgb3i iscsi connection
+ *
+ * @listhead: list head to link elements
+ * @cep: pointer to iscsi_endpoint structure
+ * @conn: pointer to iscsi_conn structure
+ * @hba: pointer to the hba this conn. is going through
+ * @task_idx_bits: # of bits needed for session->cmds_max
+ */
+struct cxgb3i_conn {
+ struct list_head list_head;
+ struct cxgb3i_endpoint *cep;
+ struct iscsi_conn *conn;
+ struct cxgb3i_hba *hba;
+ unsigned int task_idx_bits;
+};
+
+/**
+ * struct cxgb3i_endpoint - iscsi tcp endpoint
+ *
+ * @c3cn: the h/w tcp connection representation
+ * @hba: pointer to the hba this conn. is going through
+ * @cconn: pointer to the associated cxgb3i iscsi connection
+ */
+struct cxgb3i_endpoint {
+ struct s3_conn *c3cn;
+ struct cxgb3i_hba *hba;
+ struct cxgb3i_conn *cconn;
+};
+
+int cxgb3i_iscsi_init(void);
+void cxgb3i_iscsi_cleanup(void);
+
+struct cxgb3i_adapter *cxgb3i_adapter_add(struct t3cdev *);
+void cxgb3i_adapter_remove(struct t3cdev *);
+int cxgb3i_adapter_ulp_init(struct cxgb3i_adapter *);
+void cxgb3i_adapter_ulp_cleanup(struct cxgb3i_adapter *);
+
+struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *);
+struct cxgb3i_hba *cxgb3i_hba_host_add(struct cxgb3i_adapter *,
+ struct net_device *);
+void cxgb3i_hba_host_remove(struct cxgb3i_hba *);
+
+int cxgb3i_pdu_init(void);
+void cxgb3i_pdu_cleanup(void);
+void cxgb3i_conn_cleanup_task(struct iscsi_task *);
+int cxgb3i_conn_alloc_pdu(struct iscsi_task *, u8);
+int cxgb3i_conn_init_pdu(struct iscsi_task *, unsigned int, unsigned int);
+int cxgb3i_conn_xmit_pdu(struct iscsi_task *);
+
+void cxgb3i_release_itt(struct iscsi_task *task, itt_t hdr_itt);
+int cxgb3i_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt);
+
+#endif
diff --git a/drivers/scsi/cxgb3i/cxgb3i_ddp.c b/drivers/scsi/cxgb3i/cxgb3i_ddp.c
new file mode 100644
index 000000000000..1a41f04264f7
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_ddp.c
@@ -0,0 +1,770 @@
+/*
+ * cxgb3i_ddp.c: Chelsio S3xx iSCSI DDP Manager.
+ *
+ * Copyright (c) 2008 Chelsio Communications, 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.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#include <linux/skbuff.h>
+
+/* from cxgb3 LLD */
+#include "common.h"
+#include "t3_cpl.h"
+#include "t3cdev.h"
+#include "cxgb3_ctl_defs.h"
+#include "cxgb3_offload.h"
+#include "firmware_exports.h"
+
+#include "cxgb3i_ddp.h"
+
+#define DRV_MODULE_NAME "cxgb3i_ddp"
+#define DRV_MODULE_VERSION "1.0.0"
+#define DRV_MODULE_RELDATE "Dec. 1, 2008"
+
+static char version[] =
+ "Chelsio S3xx iSCSI DDP " DRV_MODULE_NAME
+ " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
+
+MODULE_AUTHOR("Karen Xie <kxie@chelsio.com>");
+MODULE_DESCRIPTION("cxgb3i ddp pagepod manager");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+#define ddp_log_error(fmt...) printk(KERN_ERR "cxgb3i_ddp: ERR! " fmt)
+#define ddp_log_warn(fmt...) printk(KERN_WARNING "cxgb3i_ddp: WARN! " fmt)
+#define ddp_log_info(fmt...) printk(KERN_INFO "cxgb3i_ddp: " fmt)
+
+#ifdef __DEBUG_CXGB3I_DDP__
+#define ddp_log_debug(fmt, args...) \
+ printk(KERN_INFO "cxgb3i_ddp: %s - " fmt, __func__ , ## args)
+#else
+#define ddp_log_debug(fmt...)
+#endif
+
+/*
+ * iSCSI Direct Data Placement
+ *
+ * T3 h/w can directly place the iSCSI Data-In or Data-Out PDU's payload into
+ * pre-posted final destination host-memory buffers based on the Initiator
+ * Task Tag (ITT) in Data-In or Target Task Tag (TTT) in Data-Out PDUs.
+ *
+ * The host memory address is programmed into h/w in the format of pagepod
+ * entries.
+ * The location of the pagepod entry is encoded into ddp tag which is used or
+ * is the base for ITT/TTT.
+ */
+
+#define DDP_PGIDX_MAX 4
+#define DDP_THRESHOLD 2048
+static unsigned char ddp_page_order[DDP_PGIDX_MAX] = {0, 1, 2, 4};
+static unsigned char ddp_page_shift[DDP_PGIDX_MAX] = {12, 13, 14, 16};
+static unsigned char page_idx = DDP_PGIDX_MAX;
+
+static LIST_HEAD(cxgb3i_ddp_list);
+static DEFINE_RWLOCK(cxgb3i_ddp_rwlock);
+
+/*
+ * functions to program the pagepod in h/w
+ */
+static inline void ulp_mem_io_set_hdr(struct sk_buff *skb, unsigned int addr)
+{
+ struct ulp_mem_io *req = (struct ulp_mem_io *)skb->head;
+
+ req->wr.wr_lo = 0;
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_BYPASS));
+ req->cmd_lock_addr = htonl(V_ULP_MEMIO_ADDR(addr >> 5) |
+ V_ULPTX_CMD(ULP_MEM_WRITE));
+ req->len = htonl(V_ULP_MEMIO_DATA_LEN(PPOD_SIZE >> 5) |
+ V_ULPTX_NFLITS((PPOD_SIZE >> 3) + 1));
+}
+
+static int set_ddp_map(struct cxgb3i_ddp_info *ddp, struct pagepod_hdr *hdr,
+ unsigned int idx, unsigned int npods,
+ struct cxgb3i_gather_list *gl)
+{
+ unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ddp->llimit;
+ int i;
+
+ for (i = 0; i < npods; i++, idx++, pm_addr += PPOD_SIZE) {
+ struct sk_buff *skb = ddp->gl_skb[idx];
+ struct pagepod *ppod;
+ int j, pidx;
+
+ /* hold on to the skb until we clear the ddp mapping */
+ skb_get(skb);
+
+ ulp_mem_io_set_hdr(skb, pm_addr);
+ ppod = (struct pagepod *)
+ (skb->head + sizeof(struct ulp_mem_io));
+ memcpy(&(ppod->hdr), hdr, sizeof(struct pagepod));
+ for (pidx = 4 * i, j = 0; j < 5; ++j, ++pidx)
+ ppod->addr[j] = pidx < gl->nelem ?
+ cpu_to_be64(gl->phys_addr[pidx]) : 0UL;
+
+ skb->priority = CPL_PRIORITY_CONTROL;
+ cxgb3_ofld_send(ddp->tdev, skb);
+ }
+ return 0;
+}
+
+static int clear_ddp_map(struct cxgb3i_ddp_info *ddp, unsigned int idx,
+ unsigned int npods)
+{
+ unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ddp->llimit;
+ int i;
+
+ for (i = 0; i < npods; i++, idx++, pm_addr += PPOD_SIZE) {
+ struct sk_buff *skb = ddp->gl_skb[idx];
+
+ ddp->gl_skb[idx] = NULL;
+ memset((skb->head + sizeof(struct ulp_mem_io)), 0, PPOD_SIZE);
+ ulp_mem_io_set_hdr(skb, pm_addr);
+ skb->priority = CPL_PRIORITY_CONTROL;
+ cxgb3_ofld_send(ddp->tdev, skb);
+ }
+ return 0;
+}
+
+static inline int ddp_find_unused_entries(struct cxgb3i_ddp_info *ddp,
+ int start, int max, int count,
+ struct cxgb3i_gather_list *gl)
+{
+ unsigned int i, j;
+
+ spin_lock(&ddp->map_lock);
+ for (i = start; i <= max;) {
+ for (j = 0; j < count; j++) {
+ if (ddp->gl_map[i + j])
+ break;
+ }
+ if (j == count) {
+ for (j = 0; j < count; j++)
+ ddp->gl_map[i + j] = gl;
+ spin_unlock(&ddp->map_lock);
+ return i;
+ }
+ i += j + 1;
+ }
+ spin_unlock(&ddp->map_lock);
+ return -EBUSY;
+}
+
+static inline void ddp_unmark_entries(struct cxgb3i_ddp_info *ddp,
+ int start, int count)
+{
+ spin_lock(&ddp->map_lock);
+ memset(&ddp->gl_map[start], 0,
+ count * sizeof(struct cxgb3i_gather_list *));
+ spin_unlock(&ddp->map_lock);
+}
+
+static inline void ddp_free_gl_skb(struct cxgb3i_ddp_info *ddp,
+ int idx, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++, idx++)
+ if (ddp->gl_skb[idx]) {
+ kfree_skb(ddp->gl_skb[idx]);
+ ddp->gl_skb[idx] = NULL;
+ }
+}
+
+static inline int ddp_alloc_gl_skb(struct cxgb3i_ddp_info *ddp, int idx,
+ int count, gfp_t gfp)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ struct sk_buff *skb = alloc_skb(sizeof(struct ulp_mem_io) +
+ PPOD_SIZE, gfp);
+ if (skb) {
+ ddp->gl_skb[idx + i] = skb;
+ skb_put(skb, sizeof(struct ulp_mem_io) + PPOD_SIZE);
+ } else {
+ ddp_free_gl_skb(ddp, idx, i);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+/**
+ * cxgb3i_ddp_find_page_index - return ddp page index for a given page size.
+ * @pgsz: page size
+ * return the ddp page index, if no match is found return DDP_PGIDX_MAX.
+ */
+int cxgb3i_ddp_find_page_index(unsigned long pgsz)
+{
+ int i;
+
+ for (i = 0; i < DDP_PGIDX_MAX; i++) {
+ if (pgsz == (1UL << ddp_page_shift[i]))
+ return i;
+ }
+ ddp_log_debug("ddp page size 0x%lx not supported.\n", pgsz);
+ return DDP_PGIDX_MAX;
+}
+EXPORT_SYMBOL_GPL(cxgb3i_ddp_find_page_index);
+
+static inline void ddp_gl_unmap(struct pci_dev *pdev,
+ struct cxgb3i_gather_list *gl)
+{
+ int i;
+
+ for (i = 0; i < gl->nelem; i++)
+ pci_unmap_page(pdev, gl->phys_addr[i], PAGE_SIZE,
+ PCI_DMA_FROMDEVICE);
+}
+
+static inline int ddp_gl_map(struct pci_dev *pdev,
+ struct cxgb3i_gather_list *gl)
+{
+ int i;
+
+ for (i = 0; i < gl->nelem; i++) {
+ gl->phys_addr[i] = pci_map_page(pdev, gl->pages[i], 0,
+ PAGE_SIZE,
+ PCI_DMA_FROMDEVICE);
+ if (unlikely(pci_dma_mapping_error(pdev, gl->phys_addr[i])))
+ goto unmap;
+ }
+
+ return i;
+
+unmap:
+ if (i) {
+ unsigned int nelem = gl->nelem;
+
+ gl->nelem = i;
+ ddp_gl_unmap(pdev, gl);
+ gl->nelem = nelem;
+ }
+ return -ENOMEM;
+}
+
+/**
+ * cxgb3i_ddp_make_gl - build ddp page buffer list
+ * @xferlen: total buffer length
+ * @sgl: page buffer scatter-gather list
+ * @sgcnt: # of page buffers
+ * @pdev: pci_dev, used for pci map
+ * @gfp: allocation mode
+ *
+ * construct a ddp page buffer list from the scsi scattergather list.
+ * coalesce buffers as much as possible, and obtain dma addresses for
+ * each page.
+ *
+ * Return the cxgb3i_gather_list constructed from the page buffers if the
+ * memory can be used for ddp. Return NULL otherwise.
+ */
+struct cxgb3i_gather_list *cxgb3i_ddp_make_gl(unsigned int xferlen,
+ struct scatterlist *sgl,
+ unsigned int sgcnt,
+ struct pci_dev *pdev,
+ gfp_t gfp)
+{
+ struct cxgb3i_gather_list *gl;
+ struct scatterlist *sg = sgl;
+ struct page *sgpage = sg_page(sg);
+ unsigned int sglen = sg->length;
+ unsigned int sgoffset = sg->offset;
+ unsigned int npages = (xferlen + sgoffset + PAGE_SIZE - 1) >>
+ PAGE_SHIFT;
+ int i = 1, j = 0;
+
+ if (xferlen < DDP_THRESHOLD) {
+ ddp_log_debug("xfer %u < threshold %u, no ddp.\n",
+ xferlen, DDP_THRESHOLD);
+ return NULL;
+ }
+
+ gl = kzalloc(sizeof(struct cxgb3i_gather_list) +
+ npages * (sizeof(dma_addr_t) + sizeof(struct page *)),
+ gfp);
+ if (!gl)
+ return NULL;
+
+ gl->pages = (struct page **)&gl->phys_addr[npages];
+ gl->length = xferlen;
+ gl->offset = sgoffset;
+ gl->pages[0] = sgpage;
+
+ sg = sg_next(sg);
+ while (sg) {
+ struct page *page = sg_page(sg);
+
+ if (sgpage == page && sg->offset == sgoffset + sglen)
+ sglen += sg->length;
+ else {
+ /* make sure the sgl is fit for ddp:
+ * each has the same page size, and
+ * all of the middle pages are used completely
+ */
+ if ((j && sgoffset) ||
+ ((i != sgcnt - 1) &&
+ ((sglen + sgoffset) & ~PAGE_MASK)))
+ goto error_out;
+
+ j++;
+ if (j == gl->nelem || sg->offset)
+ goto error_out;
+ gl->pages[j] = page;
+ sglen = sg->length;
+ sgoffset = sg->offset;
+ sgpage = page;
+ }
+ i++;
+ sg = sg_next(sg);
+ }
+ gl->nelem = ++j;
+
+ if (ddp_gl_map(pdev, gl) < 0)
+ goto error_out;
+
+ return gl;
+
+error_out:
+ kfree(gl);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(cxgb3i_ddp_make_gl);
+
+/**
+ * cxgb3i_ddp_release_gl - release a page buffer list
+ * @gl: a ddp page buffer list
+ * @pdev: pci_dev used for pci_unmap
+ * free a ddp page buffer list resulted from cxgb3i_ddp_make_gl().
+ */
+void cxgb3i_ddp_release_gl(struct cxgb3i_gather_list *gl,
+ struct pci_dev *pdev)
+{
+ ddp_gl_unmap(pdev, gl);
+ kfree(gl);
+}
+EXPORT_SYMBOL_GPL(cxgb3i_ddp_release_gl);
+
+/**
+ * cxgb3i_ddp_tag_reserve - set up ddp for a data transfer
+ * @tdev: t3cdev adapter
+ * @tid: connection id
+ * @tformat: tag format
+ * @tagp: the s/w tag, if ddp setup is successful, it will be updated with
+ * ddp/hw tag
+ * @gl: the page momory list
+ * @gfp: allocation mode
+ *
+ * ddp setup for a given page buffer list and construct the ddp tag.
+ * return 0 if success, < 0 otherwise.
+ */
+int cxgb3i_ddp_tag_reserve(struct t3cdev *tdev, unsigned int tid,
+ struct cxgb3i_tag_format *tformat, u32 *tagp,
+ struct cxgb3i_gather_list *gl, gfp_t gfp)
+{
+ struct cxgb3i_ddp_info *ddp = tdev->ulp_iscsi;
+ struct pagepod_hdr hdr;
+ unsigned int npods;
+ int idx = -1, idx_max;
+ int err = -ENOMEM;
+ u32 sw_tag = *tagp;
+ u32 tag;
+
+ if (page_idx >= DDP_PGIDX_MAX || !ddp || !gl || !gl->nelem ||
+ gl->length < DDP_THRESHOLD) {
+ ddp_log_debug("pgidx %u, xfer %u/%u, NO ddp.\n",
+ page_idx, gl->length, DDP_THRESHOLD);
+ return -EINVAL;
+ }
+
+ npods = (gl->nelem + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT;
+ idx_max = ddp->nppods - npods + 1;
+
+ if (ddp->idx_last == ddp->nppods)
+ idx = ddp_find_unused_entries(ddp, 0, idx_max, npods, gl);
+ else {
+ idx = ddp_find_unused_entries(ddp, ddp->idx_last + 1,
+ idx_max, npods, gl);
+ if (idx < 0 && ddp->idx_last >= npods)
+ idx = ddp_find_unused_entries(ddp, 0,
+ ddp->idx_last - npods + 1,
+ npods, gl);
+ }
+ if (idx < 0) {
+ ddp_log_debug("xferlen %u, gl %u, npods %u NO DDP.\n",
+ gl->length, gl->nelem, npods);
+ return idx;
+ }
+
+ err = ddp_alloc_gl_skb(ddp, idx, npods, gfp);
+ if (err < 0)
+ goto unmark_entries;
+
+ tag = cxgb3i_ddp_tag_base(tformat, sw_tag);
+ tag |= idx << PPOD_IDX_SHIFT;
+
+ hdr.rsvd = 0;
+ hdr.vld_tid = htonl(F_PPOD_VALID | V_PPOD_TID(tid));
+ hdr.pgsz_tag_clr = htonl(tag & ddp->rsvd_tag_mask);
+ hdr.maxoffset = htonl(gl->length);
+ hdr.pgoffset = htonl(gl->offset);
+
+ err = set_ddp_map(ddp, &hdr, idx, npods, gl);
+ if (err < 0)
+ goto free_gl_skb;
+
+ ddp->idx_last = idx;
+ ddp_log_debug("xfer %u, gl %u,%u, tid 0x%x, 0x%x -> 0x%x(%u,%u).\n",
+ gl->length, gl->nelem, gl->offset, tid, sw_tag, tag,
+ idx, npods);
+ *tagp = tag;
+ return 0;
+
+free_gl_skb:
+ ddp_free_gl_skb(ddp, idx, npods);
+unmark_entries:
+ ddp_unmark_entries(ddp, idx, npods);
+ return err;
+}
+EXPORT_SYMBOL_GPL(cxgb3i_ddp_tag_reserve);
+
+/**
+ * cxgb3i_ddp_tag_release - release a ddp tag
+ * @tdev: t3cdev adapter
+ * @tag: ddp tag
+ * ddp cleanup for a given ddp tag and release all the resources held
+ */
+void cxgb3i_ddp_tag_release(struct t3cdev *tdev, u32 tag)
+{
+ struct cxgb3i_ddp_info *ddp = tdev->ulp_iscsi;
+ u32 idx;
+
+ if (!ddp) {
+ ddp_log_error("release ddp tag 0x%x, ddp NULL.\n", tag);
+ return;
+ }
+
+ idx = (tag >> PPOD_IDX_SHIFT) & ddp->idx_mask;
+ if (idx < ddp->nppods) {
+ struct cxgb3i_gather_list *gl = ddp->gl_map[idx];
+ unsigned int npods;
+
+ if (!gl) {
+ ddp_log_error("release ddp 0x%x, idx 0x%x, gl NULL.\n",
+ tag, idx);
+ return;
+ }
+ npods = (gl->nelem + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT;
+ ddp_log_debug("ddp tag 0x%x, release idx 0x%x, npods %u.\n",
+ tag, idx, npods);
+ clear_ddp_map(ddp, idx, npods);
+ ddp_unmark_entries(ddp, idx, npods);
+ cxgb3i_ddp_release_gl(gl, ddp->pdev);
+ } else
+ ddp_log_error("ddp tag 0x%x, idx 0x%x > max 0x%x.\n",
+ tag, idx, ddp->nppods);
+}
+EXPORT_SYMBOL_GPL(cxgb3i_ddp_tag_release);
+
+static int setup_conn_pgidx(struct t3cdev *tdev, unsigned int tid, int pg_idx,
+ int reply)
+{
+ struct sk_buff *skb = alloc_skb(sizeof(struct cpl_set_tcb_field),
+ GFP_KERNEL);
+ struct cpl_set_tcb_field *req;
+ u64 val = pg_idx < DDP_PGIDX_MAX ? pg_idx : 0;
+
+ if (!skb)
+ return -ENOMEM;
+
+ /* set up ulp submode and page size */
+ req = (struct cpl_set_tcb_field *)skb_put(skb, sizeof(*req));
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid));
+ req->reply = V_NO_REPLY(reply ? 0 : 1);
+ req->cpu_idx = 0;
+ req->word = htons(31);
+ req->mask = cpu_to_be64(0xF0000000);
+ req->val = cpu_to_be64(val << 28);
+ skb->priority = CPL_PRIORITY_CONTROL;
+
+ cxgb3_ofld_send(tdev, skb);
+ return 0;
+}
+
+/**
+ * cxgb3i_setup_conn_host_pagesize - setup the conn.'s ddp page size
+ * @tdev: t3cdev adapter
+ * @tid: connection id
+ * @reply: request reply from h/w
+ * set up the ddp page size based on the host PAGE_SIZE for a connection
+ * identified by tid
+ */
+int cxgb3i_setup_conn_host_pagesize(struct t3cdev *tdev, unsigned int tid,
+ int reply)
+{
+ return setup_conn_pgidx(tdev, tid, page_idx, reply);
+}
+EXPORT_SYMBOL_GPL(cxgb3i_setup_conn_host_pagesize);
+
+/**
+ * cxgb3i_setup_conn_pagesize - setup the conn.'s ddp page size
+ * @tdev: t3cdev adapter
+ * @tid: connection id
+ * @reply: request reply from h/w
+ * @pgsz: ddp page size
+ * set up the ddp page size for a connection identified by tid
+ */
+int cxgb3i_setup_conn_pagesize(struct t3cdev *tdev, unsigned int tid,
+ int reply, unsigned long pgsz)
+{
+ int pgidx = cxgb3i_ddp_find_page_index(pgsz);
+
+ return setup_conn_pgidx(tdev, tid, pgidx, reply);
+}
+EXPORT_SYMBOL_GPL(cxgb3i_setup_conn_pagesize);
+
+/**
+ * cxgb3i_setup_conn_digest - setup conn. digest setting
+ * @tdev: t3cdev adapter
+ * @tid: connection id
+ * @hcrc: header digest enabled
+ * @dcrc: data digest enabled
+ * @reply: request reply from h/w
+ * set up the iscsi digest settings for a connection identified by tid
+ */
+int cxgb3i_setup_conn_digest(struct t3cdev *tdev, unsigned int tid,
+ int hcrc, int dcrc, int reply)
+{
+ struct sk_buff *skb = alloc_skb(sizeof(struct cpl_set_tcb_field),
+ GFP_KERNEL);
+ struct cpl_set_tcb_field *req;
+ u64 val = (hcrc ? 1 : 0) | (dcrc ? 2 : 0);
+
+ if (!skb)
+ return -ENOMEM;
+
+ /* set up ulp submode and page size */
+ req = (struct cpl_set_tcb_field *)skb_put(skb, sizeof(*req));
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid));
+ req->reply = V_NO_REPLY(reply ? 0 : 1);
+ req->cpu_idx = 0;
+ req->word = htons(31);
+ req->mask = cpu_to_be64(0x0F000000);
+ req->val = cpu_to_be64(val << 24);
+ skb->priority = CPL_PRIORITY_CONTROL;
+
+ cxgb3_ofld_send(tdev, skb);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cxgb3i_setup_conn_digest);
+
+static int ddp_init(struct t3cdev *tdev)
+{
+ struct cxgb3i_ddp_info *ddp;
+ struct ulp_iscsi_info uinfo;
+ unsigned int ppmax, bits;
+ int i, err;
+ static int vers_printed;
+
+ if (!vers_printed) {
+ printk(KERN_INFO "%s", version);
+ vers_printed = 1;
+ }
+
+ err = tdev->ctl(tdev, ULP_ISCSI_GET_PARAMS, &uinfo);
+ if (err < 0) {
+ ddp_log_error("%s, failed to get iscsi param err=%d.\n",
+ tdev->name, err);
+ return err;
+ }
+
+ ppmax = (uinfo.ulimit - uinfo.llimit + 1) >> PPOD_SIZE_SHIFT;
+ bits = __ilog2_u32(ppmax) + 1;
+ if (bits > PPOD_IDX_MAX_SIZE)
+ bits = PPOD_IDX_MAX_SIZE;
+ ppmax = (1 << (bits - 1)) - 1;
+
+ ddp = cxgb3i_alloc_big_mem(sizeof(struct cxgb3i_ddp_info) +
+ ppmax *
+ (sizeof(struct cxgb3i_gather_list *) +
+ sizeof(struct sk_buff *)),
+ GFP_KERNEL);
+ if (!ddp) {
+ ddp_log_warn("%s unable to alloc ddp 0x%d, ddp disabled.\n",
+ tdev->name, ppmax);
+ return 0;
+ }
+ ddp->gl_map = (struct cxgb3i_gather_list **)(ddp + 1);
+ ddp->gl_skb = (struct sk_buff **)(((char *)ddp->gl_map) +
+ ppmax *
+ sizeof(struct cxgb3i_gather_list *));
+ spin_lock_init(&ddp->map_lock);
+
+ ddp->tdev = tdev;
+ ddp->pdev = uinfo.pdev;
+ ddp->max_txsz = min_t(unsigned int, uinfo.max_txsz, ULP2_MAX_PKT_SIZE);
+ ddp->max_rxsz = min_t(unsigned int, uinfo.max_rxsz, ULP2_MAX_PKT_SIZE);
+ ddp->llimit = uinfo.llimit;
+ ddp->ulimit = uinfo.ulimit;
+ ddp->nppods = ppmax;
+ ddp->idx_last = ppmax;
+ ddp->idx_bits = bits;
+ ddp->idx_mask = (1 << bits) - 1;
+ ddp->rsvd_tag_mask = (1 << (bits + PPOD_IDX_SHIFT)) - 1;
+
+ uinfo.tagmask = ddp->idx_mask << PPOD_IDX_SHIFT;
+ for (i = 0; i < DDP_PGIDX_MAX; i++)
+ uinfo.pgsz_factor[i] = ddp_page_order[i];
+ uinfo.ulimit = uinfo.llimit + (ppmax << PPOD_SIZE_SHIFT);
+
+ err = tdev->ctl(tdev, ULP_ISCSI_SET_PARAMS, &uinfo);
+ if (err < 0) {
+ ddp_log_warn("%s unable to set iscsi param err=%d, "
+ "ddp disabled.\n", tdev->name, err);
+ goto free_ddp_map;
+ }
+
+ tdev->ulp_iscsi = ddp;
+
+ /* add to the list */
+ write_lock(&cxgb3i_ddp_rwlock);
+ list_add_tail(&ddp->list, &cxgb3i_ddp_list);
+ write_unlock(&cxgb3i_ddp_rwlock);
+
+ ddp_log_info("nppods %u (0x%x ~ 0x%x), bits %u, mask 0x%x,0x%x "
+ "pkt %u,%u.\n",
+ ppmax, ddp->llimit, ddp->ulimit, ddp->idx_bits,
+ ddp->idx_mask, ddp->rsvd_tag_mask,
+ ddp->max_txsz, ddp->max_rxsz);
+ return 0;
+
+free_ddp_map:
+ cxgb3i_free_big_mem(ddp);
+ return err;
+}
+
+/**
+ * cxgb3i_adapter_ddp_init - initialize the adapter's ddp resource
+ * @tdev: t3cdev adapter
+ * @tformat: tag format
+ * @txsz: max tx pkt size, filled in by this func.
+ * @rxsz: max rx pkt size, filled in by this func.
+ * initialize the ddp pagepod manager for a given adapter if needed and
+ * setup the tag format for a given iscsi entity
+ */
+int cxgb3i_adapter_ddp_init(struct t3cdev *tdev,
+ struct cxgb3i_tag_format *tformat,
+ unsigned int *txsz, unsigned int *rxsz)
+{
+ struct cxgb3i_ddp_info *ddp;
+ unsigned char idx_bits;
+
+ if (!tformat)
+ return -EINVAL;
+
+ if (!tdev->ulp_iscsi) {
+ int err = ddp_init(tdev);
+ if (err < 0)
+ return err;
+ }
+ ddp = (struct cxgb3i_ddp_info *)tdev->ulp_iscsi;
+
+ idx_bits = 32 - tformat->sw_bits;
+ tformat->rsvd_bits = ddp->idx_bits;
+ tformat->rsvd_shift = PPOD_IDX_SHIFT;
+ tformat->rsvd_mask = (1 << tformat->rsvd_bits) - 1;
+
+ ddp_log_info("tag format: sw %u, rsvd %u,%u, mask 0x%x.\n",
+ tformat->sw_bits, tformat->rsvd_bits,
+ tformat->rsvd_shift, tformat->rsvd_mask);
+
+ *txsz = ddp->max_txsz;
+ *rxsz = ddp->max_rxsz;
+ ddp_log_info("ddp max pkt size: %u, %u.\n",
+ ddp->max_txsz, ddp->max_rxsz);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cxgb3i_adapter_ddp_init);
+
+static void ddp_release(struct cxgb3i_ddp_info *ddp)
+{
+ int i = 0;
+ struct t3cdev *tdev = ddp->tdev;
+
+ tdev->ulp_iscsi = NULL;
+ while (i < ddp->nppods) {
+ struct cxgb3i_gather_list *gl = ddp->gl_map[i];
+ if (gl) {
+ int npods = (gl->nelem + PPOD_PAGES_MAX - 1)
+ >> PPOD_PAGES_SHIFT;
+
+ kfree(gl);
+ ddp_free_gl_skb(ddp, i, npods);
+ } else
+ i++;
+ }
+ cxgb3i_free_big_mem(ddp);
+}
+
+/**
+ * cxgb3i_adapter_ddp_cleanup - release the adapter's ddp resource
+ * @tdev: t3cdev adapter
+ * release all the resource held by the ddp pagepod manager for a given
+ * adapter if needed
+ */
+void cxgb3i_adapter_ddp_cleanup(struct t3cdev *tdev)
+{
+ struct cxgb3i_ddp_info *ddp;
+
+ /* remove from the list */
+ write_lock(&cxgb3i_ddp_rwlock);
+ list_for_each_entry(ddp, &cxgb3i_ddp_list, list) {
+ if (ddp->tdev == tdev) {
+ list_del(&ddp->list);
+ break;
+ }
+ }
+ write_unlock(&cxgb3i_ddp_rwlock);
+
+ if (ddp)
+ ddp_release(ddp);
+}
+EXPORT_SYMBOL_GPL(cxgb3i_adapter_ddp_cleanup);
+
+/**
+ * cxgb3i_ddp_init_module - module init entry point
+ * initialize any driver wide global data structures
+ */
+static int __init cxgb3i_ddp_init_module(void)
+{
+ page_idx = cxgb3i_ddp_find_page_index(PAGE_SIZE);
+ ddp_log_info("system PAGE_SIZE %lu, ddp idx %u.\n",
+ PAGE_SIZE, page_idx);
+ return 0;
+}
+
+/**
+ * cxgb3i_ddp_exit_module - module cleanup/exit entry point
+ * go through the ddp list and release any resource held.
+ */
+static void __exit cxgb3i_ddp_exit_module(void)
+{
+ struct cxgb3i_ddp_info *ddp;
+
+ /* release all ddp manager if there is any */
+ write_lock(&cxgb3i_ddp_rwlock);
+ list_for_each_entry(ddp, &cxgb3i_ddp_list, list) {
+ list_del(&ddp->list);
+ ddp_release(ddp);
+ }
+ write_unlock(&cxgb3i_ddp_rwlock);
+}
+
+module_init(cxgb3i_ddp_init_module);
+module_exit(cxgb3i_ddp_exit_module);
diff --git a/drivers/scsi/cxgb3i/cxgb3i_ddp.h b/drivers/scsi/cxgb3i/cxgb3i_ddp.h
new file mode 100644
index 000000000000..5c7c4d95c493
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_ddp.h
@@ -0,0 +1,306 @@
+/*
+ * cxgb3i_ddp.h: Chelsio S3xx iSCSI DDP Manager.
+ *
+ * Copyright (c) 2008 Chelsio Communications, 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.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#ifndef __CXGB3I_ULP2_DDP_H__
+#define __CXGB3I_ULP2_DDP_H__
+
+/**
+ * struct cxgb3i_tag_format - cxgb3i ulp tag format for an iscsi entity
+ *
+ * @sw_bits: # of bits used by iscsi software layer
+ * @rsvd_bits: # of bits used by h/w
+ * @rsvd_shift: h/w bits shift left
+ * @rsvd_mask: reserved bit mask
+ */
+struct cxgb3i_tag_format {
+ unsigned char sw_bits;
+ unsigned char rsvd_bits;
+ unsigned char rsvd_shift;
+ unsigned char filler[1];
+ u32 rsvd_mask;
+};
+
+/**
+ * struct cxgb3i_gather_list - cxgb3i direct data placement memory
+ *
+ * @tag: ddp tag
+ * @length: total data buffer length
+ * @offset: initial offset to the 1st page
+ * @nelem: # of pages
+ * @pages: page pointers
+ * @phys_addr: physical address
+ */
+struct cxgb3i_gather_list {
+ u32 tag;
+ unsigned int length;
+ unsigned int offset;
+ unsigned int nelem;
+ struct page **pages;
+ dma_addr_t phys_addr[0];
+};
+
+/**
+ * struct cxgb3i_ddp_info - cxgb3i direct data placement for pdu payload
+ *
+ * @list: list head to link elements
+ * @tdev: pointer to t3cdev used by cxgb3 driver
+ * @max_txsz: max tx packet size for ddp
+ * @max_rxsz: max rx packet size for ddp
+ * @llimit: lower bound of the page pod memory
+ * @ulimit: upper bound of the page pod memory
+ * @nppods: # of page pod entries
+ * @idx_last: page pod entry last used
+ * @idx_bits: # of bits the pagepod index would take
+ * @idx_mask: pagepod index mask
+ * @rsvd_tag_mask: tag mask
+ * @map_lock: lock to synchonize access to the page pod map
+ * @gl_map: ddp memory gather list
+ * @gl_skb: skb used to program the pagepod
+ */
+struct cxgb3i_ddp_info {
+ struct list_head list;
+ struct t3cdev *tdev;
+ struct pci_dev *pdev;
+ unsigned int max_txsz;
+ unsigned int max_rxsz;
+ unsigned int llimit;
+ unsigned int ulimit;
+ unsigned int nppods;
+ unsigned int idx_last;
+ unsigned char idx_bits;
+ unsigned char filler[3];
+ u32 idx_mask;
+ u32 rsvd_tag_mask;
+ spinlock_t map_lock;
+ struct cxgb3i_gather_list **gl_map;
+ struct sk_buff **gl_skb;
+};
+
+#define ULP2_MAX_PKT_SIZE 16224
+#define ULP2_MAX_PDU_PAYLOAD (ULP2_MAX_PKT_SIZE - ISCSI_PDU_NONPAYLOAD_MAX)
+#define PPOD_PAGES_MAX 4
+#define PPOD_PAGES_SHIFT 2 /* 4 pages per pod */
+
+/*
+ * struct pagepod_hdr, pagepod - pagepod format
+ */
+struct pagepod_hdr {
+ u32 vld_tid;
+ u32 pgsz_tag_clr;
+ u32 maxoffset;
+ u32 pgoffset;
+ u64 rsvd;
+};
+
+struct pagepod {
+ struct pagepod_hdr hdr;
+ u64 addr[PPOD_PAGES_MAX + 1];
+};
+
+#define PPOD_SIZE sizeof(struct pagepod) /* 64 */
+#define PPOD_SIZE_SHIFT 6
+
+#define PPOD_COLOR_SHIFT 0
+#define PPOD_COLOR_SIZE 6
+#define PPOD_COLOR_MASK ((1 << PPOD_COLOR_SIZE) - 1)
+
+#define PPOD_IDX_SHIFT PPOD_COLOR_SIZE
+#define PPOD_IDX_MAX_SIZE 24
+
+#define S_PPOD_TID 0
+#define M_PPOD_TID 0xFFFFFF
+#define V_PPOD_TID(x) ((x) << S_PPOD_TID)
+
+#define S_PPOD_VALID 24
+#define V_PPOD_VALID(x) ((x) << S_PPOD_VALID)
+#define F_PPOD_VALID V_PPOD_VALID(1U)
+
+#define S_PPOD_COLOR 0
+#define M_PPOD_COLOR 0x3F
+#define V_PPOD_COLOR(x) ((x) << S_PPOD_COLOR)
+
+#define S_PPOD_TAG 6
+#define M_PPOD_TAG 0xFFFFFF
+#define V_PPOD_TAG(x) ((x) << S_PPOD_TAG)
+
+#define S_PPOD_PGSZ 30
+#define M_PPOD_PGSZ 0x3
+#define V_PPOD_PGSZ(x) ((x) << S_PPOD_PGSZ)
+
+/*
+ * large memory chunk allocation/release
+ * use vmalloc() if kmalloc() fails
+ */
+static inline void *cxgb3i_alloc_big_mem(unsigned int size,
+ gfp_t gfp)
+{
+ void *p = kmalloc(size, gfp);
+ if (!p)
+ p = vmalloc(size);
+ if (p)
+ memset(p, 0, size);
+ return p;
+}
+
+static inline void cxgb3i_free_big_mem(void *addr)
+{
+ if (is_vmalloc_addr(addr))
+ vfree(addr);
+ else
+ kfree(addr);
+}
+
+/*
+ * cxgb3i ddp tag are 32 bits, it consists of reserved bits used by h/w and
+ * non-reserved bits that can be used by the iscsi s/w.
+ * The reserved bits are identified by the rsvd_bits and rsvd_shift fields
+ * in struct cxgb3i_tag_format.
+ *
+ * The upper most reserved bit can be used to check if a tag is ddp tag or not:
+ * if the bit is 0, the tag is a valid ddp tag
+ */
+
+/**
+ * cxgb3i_is_ddp_tag - check if a given tag is a hw/ddp tag
+ * @tformat: tag format information
+ * @tag: tag to be checked
+ *
+ * return true if the tag is a ddp tag, false otherwise.
+ */
+static inline int cxgb3i_is_ddp_tag(struct cxgb3i_tag_format *tformat, u32 tag)
+{
+ return !(tag & (1 << (tformat->rsvd_bits + tformat->rsvd_shift - 1)));
+}
+
+/**
+ * cxgb3i_sw_tag_usable - check if a given s/w tag has enough bits left for
+ * the reserved/hw bits
+ * @tformat: tag format information
+ * @sw_tag: s/w tag to be checked
+ *
+ * return true if the tag is a ddp tag, false otherwise.
+ */
+static inline int cxgb3i_sw_tag_usable(struct cxgb3i_tag_format *tformat,
+ u32 sw_tag)
+{
+ sw_tag >>= (32 - tformat->rsvd_bits);
+ return !sw_tag;
+}
+
+/**
+ * cxgb3i_set_non_ddp_tag - mark a given s/w tag as an invalid ddp tag
+ * @tformat: tag format information
+ * @sw_tag: s/w tag to be checked
+ *
+ * insert 1 at the upper most reserved bit to mark it as an invalid ddp tag.
+ */
+static inline u32 cxgb3i_set_non_ddp_tag(struct cxgb3i_tag_format *tformat,
+ u32 sw_tag)
+{
+ unsigned char shift = tformat->rsvd_bits + tformat->rsvd_shift - 1;
+ u32 mask = (1 << shift) - 1;
+
+ if (sw_tag && (sw_tag & ~mask)) {
+ u32 v1 = sw_tag & ((1 << shift) - 1);
+ u32 v2 = (sw_tag >> (shift - 1)) << shift;
+
+ return v2 | v1 | 1 << shift;
+ }
+ return sw_tag | 1 << shift;
+}
+
+/**
+ * cxgb3i_ddp_tag_base - shift the s/w tag bits so that reserved bits are not
+ * used.
+ * @tformat: tag format information
+ * @sw_tag: s/w tag to be checked
+ */
+static inline u32 cxgb3i_ddp_tag_base(struct cxgb3i_tag_format *tformat,
+ u32 sw_tag)
+{
+ u32 mask = (1 << tformat->rsvd_shift) - 1;
+
+ if (sw_tag && (sw_tag & ~mask)) {
+ u32 v1 = sw_tag & mask;
+ u32 v2 = sw_tag >> tformat->rsvd_shift;
+
+ v2 <<= tformat->rsvd_shift + tformat->rsvd_bits;
+ return v2 | v1;
+ }
+ return sw_tag;
+}
+
+/**
+ * cxgb3i_tag_rsvd_bits - get the reserved bits used by the h/w
+ * @tformat: tag format information
+ * @tag: tag to be checked
+ *
+ * return the reserved bits in the tag
+ */
+static inline u32 cxgb3i_tag_rsvd_bits(struct cxgb3i_tag_format *tformat,
+ u32 tag)
+{
+ if (cxgb3i_is_ddp_tag(tformat, tag))
+ return (tag >> tformat->rsvd_shift) & tformat->rsvd_mask;
+ return 0;
+}
+
+/**
+ * cxgb3i_tag_nonrsvd_bits - get the non-reserved bits used by the s/w
+ * @tformat: tag format information
+ * @tag: tag to be checked
+ *
+ * return the non-reserved bits in the tag.
+ */
+static inline u32 cxgb3i_tag_nonrsvd_bits(struct cxgb3i_tag_format *tformat,
+ u32 tag)
+{
+ unsigned char shift = tformat->rsvd_bits + tformat->rsvd_shift - 1;
+ u32 v1, v2;
+
+ if (cxgb3i_is_ddp_tag(tformat, tag)) {
+ v1 = tag & ((1 << tformat->rsvd_shift) - 1);
+ v2 = (tag >> (shift + 1)) << tformat->rsvd_shift;
+ } else {
+ u32 mask = (1 << shift) - 1;
+
+ tag &= ~(1 << shift);
+ v1 = tag & mask;
+ v2 = (tag >> 1) & ~mask;
+ }
+ return v1 | v2;
+}
+
+int cxgb3i_ddp_tag_reserve(struct t3cdev *, unsigned int tid,
+ struct cxgb3i_tag_format *, u32 *tag,
+ struct cxgb3i_gather_list *, gfp_t gfp);
+void cxgb3i_ddp_tag_release(struct t3cdev *, u32 tag);
+
+struct cxgb3i_gather_list *cxgb3i_ddp_make_gl(unsigned int xferlen,
+ struct scatterlist *sgl,
+ unsigned int sgcnt,
+ struct pci_dev *pdev,
+ gfp_t gfp);
+void cxgb3i_ddp_release_gl(struct cxgb3i_gather_list *gl,
+ struct pci_dev *pdev);
+
+int cxgb3i_setup_conn_host_pagesize(struct t3cdev *, unsigned int tid,
+ int reply);
+int cxgb3i_setup_conn_pagesize(struct t3cdev *, unsigned int tid, int reply,
+ unsigned long pgsz);
+int cxgb3i_setup_conn_digest(struct t3cdev *, unsigned int tid,
+ int hcrc, int dcrc, int reply);
+int cxgb3i_ddp_find_page_index(unsigned long pgsz);
+int cxgb3i_adapter_ddp_init(struct t3cdev *, struct cxgb3i_tag_format *,
+ unsigned int *txsz, unsigned int *rxsz);
+void cxgb3i_adapter_ddp_cleanup(struct t3cdev *);
+#endif
diff --git a/drivers/scsi/cxgb3i/cxgb3i_init.c b/drivers/scsi/cxgb3i/cxgb3i_init.c
new file mode 100644
index 000000000000..091ecb4d9f3d
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_init.c
@@ -0,0 +1,107 @@
+/* cxgb3i_init.c: Chelsio S3xx iSCSI driver.
+ *
+ * Copyright (c) 2008 Chelsio Communications, 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.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#include "cxgb3i.h"
+
+#define DRV_MODULE_NAME "cxgb3i"
+#define DRV_MODULE_VERSION "1.0.0"
+#define DRV_MODULE_RELDATE "Jun. 1, 2008"
+
+static char version[] =
+ "Chelsio S3xx iSCSI Driver " DRV_MODULE_NAME
+ " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
+
+MODULE_AUTHOR("Karen Xie <kxie@chelsio.com>");
+MODULE_DESCRIPTION("Chelsio S3xx iSCSI Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+static void open_s3_dev(struct t3cdev *);
+static void close_s3_dev(struct t3cdev *);
+
+static cxgb3_cpl_handler_func cxgb3i_cpl_handlers[NUM_CPL_CMDS];
+static struct cxgb3_client t3c_client = {
+ .name = "iscsi_cxgb3",
+ .handlers = cxgb3i_cpl_handlers,
+ .add = open_s3_dev,
+ .remove = close_s3_dev,
+};
+
+/**
+ * open_s3_dev - register with cxgb3 LLD
+ * @t3dev: cxgb3 adapter instance
+ */
+static void open_s3_dev(struct t3cdev *t3dev)
+{
+ static int vers_printed;
+
+ if (!vers_printed) {
+ printk(KERN_INFO "%s", version);
+ vers_printed = 1;
+ }
+
+ cxgb3i_sdev_add(t3dev, &t3c_client);
+ cxgb3i_adapter_add(t3dev);
+}
+
+/**
+ * close_s3_dev - de-register with cxgb3 LLD
+ * @t3dev: cxgb3 adapter instance
+ */
+static void close_s3_dev(struct t3cdev *t3dev)
+{
+ cxgb3i_adapter_remove(t3dev);
+ cxgb3i_sdev_remove(t3dev);
+}
+
+/**
+ * cxgb3i_init_module - module init entry point
+ *
+ * initialize any driver wide global data structures and register itself
+ * with the cxgb3 module
+ */
+static int __init cxgb3i_init_module(void)
+{
+ int err;
+
+ err = cxgb3i_sdev_init(cxgb3i_cpl_handlers);
+ if (err < 0)
+ return err;
+
+ err = cxgb3i_iscsi_init();
+ if (err < 0)
+ return err;
+
+ err = cxgb3i_pdu_init();
+ if (err < 0)
+ return err;
+
+ cxgb3_register_client(&t3c_client);
+
+ return 0;
+}
+
+/**
+ * cxgb3i_exit_module - module cleanup/exit entry point
+ *
+ * go through the driver hba list and for each hba, release any resource held.
+ * and unregisters iscsi transport and the cxgb3 module
+ */
+static void __exit cxgb3i_exit_module(void)
+{
+ cxgb3_unregister_client(&t3c_client);
+ cxgb3i_pdu_cleanup();
+ cxgb3i_iscsi_cleanup();
+ cxgb3i_sdev_cleanup();
+}
+
+module_init(cxgb3i_init_module);
+module_exit(cxgb3i_exit_module);
diff --git a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
new file mode 100644
index 000000000000..d83464b9b3f9
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
@@ -0,0 +1,951 @@
+/* cxgb3i_iscsi.c: Chelsio S3xx iSCSI driver.
+ *
+ * Copyright (c) 2008 Chelsio Communications, Inc.
+ * Copyright (c) 2008 Mike Christie
+ * Copyright (c) 2008 Red Hat, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#include <linux/inet.h>
+#include <linux/crypto.h>
+#include <net/tcp.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi.h>
+#include <scsi/iscsi_proto.h>
+#include <scsi/libiscsi.h>
+#include <scsi/scsi_transport_iscsi.h>
+
+#include "cxgb3i.h"
+#include "cxgb3i_pdu.h"
+
+#ifdef __DEBUG_CXGB3I_TAG__
+#define cxgb3i_tag_debug cxgb3i_log_debug
+#else
+#define cxgb3i_tag_debug(fmt...)
+#endif
+
+#ifdef __DEBUG_CXGB3I_API__
+#define cxgb3i_api_debug cxgb3i_log_debug
+#else
+#define cxgb3i_api_debug(fmt...)
+#endif
+
+/*
+ * align pdu size to multiple of 512 for better performance
+ */
+#define align_pdu_size(n) do { n = (n) & (~511); } while (0)
+
+static struct scsi_transport_template *cxgb3i_scsi_transport;
+static struct scsi_host_template cxgb3i_host_template;
+static struct iscsi_transport cxgb3i_iscsi_transport;
+static unsigned char sw_tag_idx_bits;
+static unsigned char sw_tag_age_bits;
+
+static LIST_HEAD(cxgb3i_snic_list);
+static DEFINE_RWLOCK(cxgb3i_snic_rwlock);
+
+/**
+ * cxgb3i_adapter_add - init a s3 adapter structure and any h/w settings
+ * @t3dev: t3cdev adapter
+ * return the resulting cxgb3i_adapter struct
+ */
+struct cxgb3i_adapter *cxgb3i_adapter_add(struct t3cdev *t3dev)
+{
+ struct cxgb3i_adapter *snic;
+ struct adapter *adapter = tdev2adap(t3dev);
+ int i;
+
+ snic = kzalloc(sizeof(*snic), GFP_KERNEL);
+ if (!snic) {
+ cxgb3i_api_debug("cxgb3 %s, OOM.\n", t3dev->name);
+ return NULL;
+ }
+ spin_lock_init(&snic->lock);
+
+ snic->tdev = t3dev;
+ snic->pdev = adapter->pdev;
+ snic->tag_format.sw_bits = sw_tag_idx_bits + sw_tag_age_bits;
+
+ if (cxgb3i_adapter_ddp_init(t3dev, &snic->tag_format,
+ &snic->tx_max_size,
+ &snic->rx_max_size) < 0)
+ goto free_snic;
+
+ for_each_port(adapter, i) {
+ snic->hba[i] = cxgb3i_hba_host_add(snic, adapter->port[i]);
+ if (!snic->hba[i])
+ goto ulp_cleanup;
+ }
+ snic->hba_cnt = adapter->params.nports;
+
+ /* add to the list */
+ write_lock(&cxgb3i_snic_rwlock);
+ list_add_tail(&snic->list_head, &cxgb3i_snic_list);
+ write_unlock(&cxgb3i_snic_rwlock);
+
+ return snic;
+
+ulp_cleanup:
+ cxgb3i_adapter_ddp_cleanup(t3dev);
+free_snic:
+ kfree(snic);
+ return NULL;
+}
+
+/**
+ * cxgb3i_adapter_remove - release all the resources held and cleanup any
+ * h/w settings
+ * @t3dev: t3cdev adapter
+ */
+void cxgb3i_adapter_remove(struct t3cdev *t3dev)
+{
+ int i;
+ struct cxgb3i_adapter *snic;
+
+ /* remove from the list */
+ write_lock(&cxgb3i_snic_rwlock);
+ list_for_each_entry(snic, &cxgb3i_snic_list, list_head) {
+ if (snic->tdev == t3dev) {
+ list_del(&snic->list_head);
+ break;
+ }
+ }
+ write_unlock(&cxgb3i_snic_rwlock);
+
+ if (snic) {
+ for (i = 0; i < snic->hba_cnt; i++) {
+ if (snic->hba[i]) {
+ cxgb3i_hba_host_remove(snic->hba[i]);
+ snic->hba[i] = NULL;
+ }
+ }
+
+ /* release ddp resources */
+ cxgb3i_adapter_ddp_cleanup(snic->tdev);
+ kfree(snic);
+ }
+}
+
+/**
+ * cxgb3i_hba_find_by_netdev - find the cxgb3i_hba structure with a given
+ * net_device
+ * @t3dev: t3cdev adapter
+ */
+struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *ndev)
+{
+ struct cxgb3i_adapter *snic;
+ int i;
+
+ read_lock(&cxgb3i_snic_rwlock);
+ list_for_each_entry(snic, &cxgb3i_snic_list, list_head) {
+ for (i = 0; i < snic->hba_cnt; i++) {
+ if (snic->hba[i]->ndev == ndev) {
+ read_unlock(&cxgb3i_snic_rwlock);
+ return snic->hba[i];
+ }
+ }
+ }
+ read_unlock(&cxgb3i_snic_rwlock);
+ return NULL;
+}
+
+/**
+ * cxgb3i_hba_host_add - register a new host with scsi/iscsi
+ * @snic: the cxgb3i adapter
+ * @ndev: associated net_device
+ */
+struct cxgb3i_hba *cxgb3i_hba_host_add(struct cxgb3i_adapter *snic,
+ struct net_device *ndev)
+{
+ struct cxgb3i_hba *hba;
+ struct Scsi_Host *shost;
+ int err;
+
+ shost = iscsi_host_alloc(&cxgb3i_host_template,
+ sizeof(struct cxgb3i_hba),
+ CXGB3I_SCSI_QDEPTH_DFLT);
+ if (!shost) {
+ cxgb3i_log_info("iscsi_host_alloc failed.\n");
+ return NULL;
+ }
+
+ shost->transportt = cxgb3i_scsi_transport;
+ shost->max_lun = CXGB3I_MAX_LUN;
+ shost->max_id = CXGB3I_MAX_TARGET;
+ shost->max_channel = 0;
+ shost->max_cmd_len = 16;
+
+ hba = iscsi_host_priv(shost);
+ hba->snic = snic;
+ hba->ndev = ndev;
+ hba->shost = shost;
+
+ pci_dev_get(snic->pdev);
+ err = iscsi_host_add(shost, &snic->pdev->dev);
+ if (err) {
+ cxgb3i_log_info("iscsi_host_add failed.\n");
+ goto pci_dev_put;
+ }
+
+ cxgb3i_api_debug("shost 0x%p, hba 0x%p, no %u.\n",
+ shost, hba, shost->host_no);
+
+ return hba;
+
+pci_dev_put:
+ pci_dev_put(snic->pdev);
+ scsi_host_put(shost);
+ return NULL;
+}
+
+/**
+ * cxgb3i_hba_host_remove - de-register the host with scsi/iscsi
+ * @hba: the cxgb3i hba
+ */
+void cxgb3i_hba_host_remove(struct cxgb3i_hba *hba)
+{
+ cxgb3i_api_debug("shost 0x%p, hba 0x%p, no %u.\n",
+ hba->shost, hba, hba->shost->host_no);
+ iscsi_host_remove(hba->shost);
+ pci_dev_put(hba->snic->pdev);
+ iscsi_host_free(hba->shost);
+}
+
+/**
+ * cxgb3i_ep_connect - establish TCP connection to target portal
+ * @dst_addr: target IP address
+ * @non_blocking: blocking or non-blocking call
+ *
+ * Initiates a TCP/IP connection to the dst_addr
+ */
+static struct iscsi_endpoint *cxgb3i_ep_connect(struct sockaddr *dst_addr,
+ int non_blocking)
+{
+ struct iscsi_endpoint *ep;
+ struct cxgb3i_endpoint *cep;
+ struct cxgb3i_hba *hba;
+ struct s3_conn *c3cn = NULL;
+ int err = 0;
+
+ c3cn = cxgb3i_c3cn_create();
+ if (!c3cn) {
+ cxgb3i_log_info("ep connect OOM.\n");
+ err = -ENOMEM;
+ goto release_conn;
+ }
+
+ err = cxgb3i_c3cn_connect(c3cn, (struct sockaddr_in *)dst_addr);
+ if (err < 0) {
+ cxgb3i_log_info("ep connect failed.\n");
+ goto release_conn;
+ }
+ hba = cxgb3i_hba_find_by_netdev(c3cn->dst_cache->dev);
+ if (!hba) {
+ err = -ENOSPC;
+ cxgb3i_log_info("NOT going through cxgbi device.\n");
+ goto release_conn;
+ }
+ if (c3cn_is_closing(c3cn)) {
+ err = -ENOSPC;
+ cxgb3i_log_info("ep connect unable to connect.\n");
+ goto release_conn;
+ }
+
+ ep = iscsi_create_endpoint(sizeof(*cep));
+ if (!ep) {
+ err = -ENOMEM;
+ cxgb3i_log_info("iscsi alloc ep, OOM.\n");
+ goto release_conn;
+ }
+ cep = ep->dd_data;
+ cep->c3cn = c3cn;
+ cep->hba = hba;
+
+ cxgb3i_api_debug("ep 0x%p, 0x%p, c3cn 0x%p, hba 0x%p.\n",
+ ep, cep, c3cn, hba);
+ return ep;
+
+release_conn:
+ cxgb3i_api_debug("conn 0x%p failed, release.\n", c3cn);
+ if (c3cn)
+ cxgb3i_c3cn_release(c3cn);
+ return ERR_PTR(err);
+}
+
+/**
+ * cxgb3i_ep_poll - polls for TCP connection establishement
+ * @ep: TCP connection (endpoint) handle
+ * @timeout_ms: timeout value in milli secs
+ *
+ * polls for TCP connect request to complete
+ */
+static int cxgb3i_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
+{
+ struct cxgb3i_endpoint *cep = ep->dd_data;
+ struct s3_conn *c3cn = cep->c3cn;
+
+ if (!c3cn_is_established(c3cn))
+ return 0;
+ cxgb3i_api_debug("ep 0x%p, c3cn 0x%p established.\n", ep, c3cn);
+ return 1;
+}
+
+/**
+ * cxgb3i_ep_disconnect - teardown TCP connection
+ * @ep: TCP connection (endpoint) handle
+ *
+ * teardown TCP connection
+ */
+static void cxgb3i_ep_disconnect(struct iscsi_endpoint *ep)
+{
+ struct cxgb3i_endpoint *cep = ep->dd_data;
+ struct cxgb3i_conn *cconn = cep->cconn;
+
+ cxgb3i_api_debug("ep 0x%p, cep 0x%p.\n", ep, cep);
+
+ if (cconn && cconn->conn) {
+ /*
+ * stop the xmit path so the xmit_pdu function is
+ * not being called
+ */
+ iscsi_suspend_tx(cconn->conn);
+
+ write_lock_bh(&cep->c3cn->callback_lock);
+ cep->c3cn->user_data = NULL;
+ cconn->cep = NULL;
+ write_unlock_bh(&cep->c3cn->callback_lock);
+ }
+
+ cxgb3i_api_debug("ep 0x%p, cep 0x%p, release c3cn 0x%p.\n",
+ ep, cep, cep->c3cn);
+ cxgb3i_c3cn_release(cep->c3cn);
+ iscsi_destroy_endpoint(ep);
+}
+
+/**
+ * cxgb3i_session_create - create a new iscsi session
+ * @cmds_max: max # of commands
+ * @qdepth: scsi queue depth
+ * @initial_cmdsn: initial iscsi CMDSN for this session
+ * @host_no: pointer to return host no
+ *
+ * Creates a new iSCSI session
+ */
+static struct iscsi_cls_session *
+cxgb3i_session_create(struct iscsi_endpoint *ep, u16 cmds_max, u16 qdepth,
+ u32 initial_cmdsn, u32 *host_no)
+{
+ struct cxgb3i_endpoint *cep;
+ struct cxgb3i_hba *hba;
+ struct Scsi_Host *shost;
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+
+ if (!ep) {
+ cxgb3i_log_error("%s, missing endpoint.\n", __func__);
+ return NULL;
+ }
+
+ cep = ep->dd_data;
+ hba = cep->hba;
+ shost = hba->shost;
+ cxgb3i_api_debug("ep 0x%p, cep 0x%p, hba 0x%p.\n", ep, cep, hba);
+ BUG_ON(hba != iscsi_host_priv(shost));
+
+ *host_no = shost->host_no;
+
+ cls_session = iscsi_session_setup(&cxgb3i_iscsi_transport, shost,
+ cmds_max,
+ sizeof(struct iscsi_tcp_task),
+ initial_cmdsn, ISCSI_MAX_TARGET);
+ if (!cls_session)
+ return NULL;
+ session = cls_session->dd_data;
+ if (iscsi_tcp_r2tpool_alloc(session))
+ goto remove_session;
+
+ return cls_session;
+
+remove_session:
+ iscsi_session_teardown(cls_session);
+ return NULL;
+}
+
+/**
+ * cxgb3i_session_destroy - destroys iscsi session
+ * @cls_session: pointer to iscsi cls session
+ *
+ * Destroys an iSCSI session instance and releases its all resources held
+ */
+static void cxgb3i_session_destroy(struct iscsi_cls_session *cls_session)
+{
+ cxgb3i_api_debug("sess 0x%p.\n", cls_session);
+ iscsi_tcp_r2tpool_free(cls_session->dd_data);
+ iscsi_session_teardown(cls_session);
+}
+
+/**
+ * cxgb3i_conn_max_xmit_dlength -- check the max. xmit pdu segment size,
+ * reduce it to be within the hardware limit if needed
+ * @conn: iscsi connection
+ */
+static inline int cxgb3i_conn_max_xmit_dlength(struct iscsi_conn *conn)
+
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ unsigned int max = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD,
+ cconn->hba->snic->tx_max_size -
+ ISCSI_PDU_NONPAYLOAD_MAX);
+
+ if (conn->max_xmit_dlength)
+ conn->max_xmit_dlength = min_t(unsigned int,
+ conn->max_xmit_dlength, max);
+ else
+ conn->max_xmit_dlength = max;
+ align_pdu_size(conn->max_xmit_dlength);
+ cxgb3i_log_info("conn 0x%p, max xmit %u.\n",
+ conn, conn->max_xmit_dlength);
+ return 0;
+}
+
+/**
+ * cxgb3i_conn_max_recv_dlength -- check the max. recv pdu segment size against
+ * the hardware limit
+ * @conn: iscsi connection
+ * return 0 if the value is valid, < 0 otherwise.
+ */
+static inline int cxgb3i_conn_max_recv_dlength(struct iscsi_conn *conn)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ unsigned int max = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD,
+ cconn->hba->snic->rx_max_size -
+ ISCSI_PDU_NONPAYLOAD_MAX);
+
+ align_pdu_size(max);
+ if (conn->max_recv_dlength) {
+ if (conn->max_recv_dlength > max) {
+ cxgb3i_log_error("MaxRecvDataSegmentLength %u too big."
+ " Need to be <= %u.\n",
+ conn->max_recv_dlength, max);
+ return -EINVAL;
+ }
+ conn->max_recv_dlength = min_t(unsigned int,
+ conn->max_recv_dlength, max);
+ align_pdu_size(conn->max_recv_dlength);
+ } else
+ conn->max_recv_dlength = max;
+ cxgb3i_api_debug("conn 0x%p, max recv %u.\n",
+ conn, conn->max_recv_dlength);
+ return 0;
+}
+
+/**
+ * cxgb3i_conn_create - create iscsi connection instance
+ * @cls_session: pointer to iscsi cls session
+ * @cid: iscsi cid
+ *
+ * Creates a new iSCSI connection instance for a given session
+ */
+static struct iscsi_cls_conn *cxgb3i_conn_create(struct iscsi_cls_session
+ *cls_session, u32 cid)
+{
+ struct iscsi_cls_conn *cls_conn;
+ struct iscsi_conn *conn;
+ struct iscsi_tcp_conn *tcp_conn;
+ struct cxgb3i_conn *cconn;
+
+ cxgb3i_api_debug("sess 0x%p, cid %u.\n", cls_session, cid);
+
+ cls_conn = iscsi_tcp_conn_setup(cls_session, sizeof(*cconn), cid);
+ if (!cls_conn)
+ return NULL;
+ conn = cls_conn->dd_data;
+ tcp_conn = conn->dd_data;
+ cconn = tcp_conn->dd_data;
+
+ cconn->conn = conn;
+ return cls_conn;
+}
+
+/**
+ * cxgb3i_conn_bind - binds iscsi sess, conn and endpoint together
+ * @cls_session: pointer to iscsi cls session
+ * @cls_conn: pointer to iscsi cls conn
+ * @transport_eph: 64-bit EP handle
+ * @is_leading: leading connection on this session?
+ *
+ * Binds together an iSCSI session, an iSCSI connection and a
+ * TCP connection. This routine returns error code if the TCP
+ * connection does not belong on the device iSCSI sess/conn is bound
+ */
+
+static int cxgb3i_conn_bind(struct iscsi_cls_session *cls_session,
+ struct iscsi_cls_conn *cls_conn,
+ u64 transport_eph, int is_leading)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ struct cxgb3i_adapter *snic;
+ struct iscsi_endpoint *ep;
+ struct cxgb3i_endpoint *cep;
+ struct s3_conn *c3cn;
+ int err;
+
+ ep = iscsi_lookup_endpoint(transport_eph);
+ if (!ep)
+ return -EINVAL;
+
+ /* setup ddp pagesize */
+ cep = ep->dd_data;
+ c3cn = cep->c3cn;
+ snic = cep->hba->snic;
+ err = cxgb3i_setup_conn_host_pagesize(snic->tdev, c3cn->tid, 0);
+ if (err < 0)
+ return err;
+
+ cxgb3i_api_debug("ep 0x%p, cls sess 0x%p, cls conn 0x%p.\n",
+ ep, cls_session, cls_conn);
+
+ err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
+ if (err)
+ return -EINVAL;
+
+ /* calculate the tag idx bits needed for this conn based on cmds_max */
+ cconn->task_idx_bits = (__ilog2_u32(conn->session->cmds_max - 1)) + 1;
+ cxgb3i_api_debug("session cmds_max 0x%x, bits %u.\n",
+ conn->session->cmds_max, cconn->task_idx_bits);
+
+ read_lock(&c3cn->callback_lock);
+ c3cn->user_data = conn;
+ cconn->hba = cep->hba;
+ cconn->cep = cep;
+ cep->cconn = cconn;
+ read_unlock(&c3cn->callback_lock);
+
+ cxgb3i_conn_max_xmit_dlength(conn);
+ cxgb3i_conn_max_recv_dlength(conn);
+
+ spin_lock_bh(&conn->session->lock);
+ sprintf(conn->portal_address, NIPQUAD_FMT,
+ NIPQUAD(c3cn->daddr.sin_addr.s_addr));
+ conn->portal_port = ntohs(c3cn->daddr.sin_port);
+ spin_unlock_bh(&conn->session->lock);
+
+ /* init recv engine */
+ iscsi_tcp_hdr_recv_prep(tcp_conn);
+
+ return 0;
+}
+
+/**
+ * cxgb3i_conn_get_param - return iscsi connection parameter to caller
+ * @cls_conn: pointer to iscsi cls conn
+ * @param: parameter type identifier
+ * @buf: buffer pointer
+ *
+ * returns iSCSI connection parameters
+ */
+static int cxgb3i_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ int len;
+
+ cxgb3i_api_debug("cls_conn 0x%p, param %d.\n", cls_conn, param);
+
+ switch (param) {
+ case ISCSI_PARAM_CONN_PORT:
+ spin_lock_bh(&conn->session->lock);
+ len = sprintf(buf, "%hu\n", conn->portal_port);
+ spin_unlock_bh(&conn->session->lock);
+ break;
+ case ISCSI_PARAM_CONN_ADDRESS:
+ spin_lock_bh(&conn->session->lock);
+ len = sprintf(buf, "%s\n", conn->portal_address);
+ spin_unlock_bh(&conn->session->lock);
+ break;
+ default:
+ return iscsi_conn_get_param(cls_conn, param, buf);
+ }
+
+ return len;
+}
+
+/**
+ * cxgb3i_conn_set_param - set iscsi connection parameter
+ * @cls_conn: pointer to iscsi cls conn
+ * @param: parameter type identifier
+ * @buf: buffer pointer
+ * @buflen: buffer length
+ *
+ * set iSCSI connection parameters
+ */
+static int cxgb3i_conn_set_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf, int buflen)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ struct cxgb3i_adapter *snic = cconn->hba->snic;
+ struct s3_conn *c3cn = cconn->cep->c3cn;
+ int value, err = 0;
+
+ switch (param) {
+ case ISCSI_PARAM_HDRDGST_EN:
+ err = iscsi_set_param(cls_conn, param, buf, buflen);
+ if (!err && conn->hdrdgst_en)
+ err = cxgb3i_setup_conn_digest(snic->tdev, c3cn->tid,
+ conn->hdrdgst_en,
+ conn->datadgst_en, 0);
+ break;
+ case ISCSI_PARAM_DATADGST_EN:
+ err = iscsi_set_param(cls_conn, param, buf, buflen);
+ if (!err && conn->datadgst_en)
+ err = cxgb3i_setup_conn_digest(snic->tdev, c3cn->tid,
+ conn->hdrdgst_en,
+ conn->datadgst_en, 0);
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ sscanf(buf, "%d", &value);
+ if (value <= 0 || !is_power_of_2(value))
+ return -EINVAL;
+ if (session->max_r2t == value)
+ break;
+ iscsi_tcp_r2tpool_free(session);
+ err = iscsi_set_param(cls_conn, param, buf, buflen);
+ if (!err && iscsi_tcp_r2tpool_alloc(session))
+ return -ENOMEM;
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ err = iscsi_set_param(cls_conn, param, buf, buflen);
+ if (!err)
+ err = cxgb3i_conn_max_recv_dlength(conn);
+ break;
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ err = iscsi_set_param(cls_conn, param, buf, buflen);
+ if (!err)
+ err = cxgb3i_conn_max_xmit_dlength(conn);
+ break;
+ default:
+ return iscsi_set_param(cls_conn, param, buf, buflen);
+ }
+ return err;
+}
+
+/**
+ * cxgb3i_host_set_param - configure host (adapter) related parameters
+ * @shost: scsi host pointer
+ * @param: parameter type identifier
+ * @buf: buffer pointer
+ */
+static int cxgb3i_host_set_param(struct Scsi_Host *shost,
+ enum iscsi_host_param param,
+ char *buf, int buflen)
+{
+ struct cxgb3i_hba *hba = iscsi_host_priv(shost);
+
+ cxgb3i_api_debug("param %d, buf %s.\n", param, buf);
+
+ switch (param) {
+ case ISCSI_HOST_PARAM_IPADDRESS:
+ {
+ __be32 addr = in_aton(buf);
+ cxgb3i_set_private_ipv4addr(hba->ndev, addr);
+ return 0;
+ }
+ case ISCSI_HOST_PARAM_HWADDRESS:
+ case ISCSI_HOST_PARAM_NETDEV_NAME:
+ /* ignore */
+ return 0;
+ default:
+ return iscsi_host_set_param(shost, param, buf, buflen);
+ }
+}
+
+/**
+ * cxgb3i_host_get_param - returns host (adapter) related parameters
+ * @shost: scsi host pointer
+ * @param: parameter type identifier
+ * @buf: buffer pointer
+ */
+static int cxgb3i_host_get_param(struct Scsi_Host *shost,
+ enum iscsi_host_param param, char *buf)
+{
+ struct cxgb3i_hba *hba = iscsi_host_priv(shost);
+ int len = 0;
+
+ cxgb3i_api_debug("hba %s, param %d.\n", hba->ndev->name, param);
+
+ switch (param) {
+ case ISCSI_HOST_PARAM_HWADDRESS:
+ len = sysfs_format_mac(buf, hba->ndev->dev_addr, 6);
+ break;
+ case ISCSI_HOST_PARAM_NETDEV_NAME:
+ len = sprintf(buf, "%s\n", hba->ndev->name);
+ break;
+ case ISCSI_HOST_PARAM_IPADDRESS:
+ {
+ __be32 addr;
+
+ addr = cxgb3i_get_private_ipv4addr(hba->ndev);
+ len = sprintf(buf, NIPQUAD_FMT, NIPQUAD(addr));
+ break;
+ }
+ default:
+ return iscsi_host_get_param(shost, param, buf);
+ }
+ return len;
+}
+
+/**
+ * cxgb3i_conn_get_stats - returns iSCSI stats
+ * @cls_conn: pointer to iscsi cls conn
+ * @stats: pointer to iscsi statistic struct
+ */
+static void cxgb3i_conn_get_stats(struct iscsi_cls_conn *cls_conn,
+ struct iscsi_stats *stats)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+
+ stats->txdata_octets = conn->txdata_octets;
+ stats->rxdata_octets = conn->rxdata_octets;
+ stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
+ stats->dataout_pdus = conn->dataout_pdus_cnt;
+ stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
+ stats->datain_pdus = conn->datain_pdus_cnt;
+ stats->r2t_pdus = conn->r2t_pdus_cnt;
+ stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
+ stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
+ stats->digest_err = 0;
+ stats->timeout_err = 0;
+ stats->custom_length = 1;
+ strcpy(stats->custom[0].desc, "eh_abort_cnt");
+ stats->custom[0].value = conn->eh_abort_cnt;
+}
+
+/**
+ * cxgb3i_parse_itt - get the idx and age bits from a given tag
+ * @conn: iscsi connection
+ * @itt: itt tag
+ * @idx: task index, filled in by this function
+ * @age: session age, filled in by this function
+ */
+static void cxgb3i_parse_itt(struct iscsi_conn *conn, itt_t itt,
+ int *idx, int *age)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ struct cxgb3i_adapter *snic = cconn->hba->snic;
+ u32 tag = ntohl((__force u32) itt);
+ u32 sw_bits;
+
+ sw_bits = cxgb3i_tag_nonrsvd_bits(&snic->tag_format, tag);
+ if (idx)
+ *idx = sw_bits & ((1 << cconn->task_idx_bits) - 1);
+ if (age)
+ *age = (sw_bits >> cconn->task_idx_bits) & ISCSI_AGE_MASK;
+
+ cxgb3i_tag_debug("parse tag 0x%x/0x%x, sw 0x%x, itt 0x%x, age 0x%x.\n",
+ tag, itt, sw_bits, idx ? *idx : 0xFFFFF,
+ age ? *age : 0xFF);
+}
+
+/**
+ * cxgb3i_reserve_itt - generate tag for a give task
+ * Try to set up ddp for a scsi read task.
+ * @task: iscsi task
+ * @hdr_itt: tag, filled in by this function
+ */
+int cxgb3i_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt)
+{
+ struct scsi_cmnd *sc = task->sc;
+ struct iscsi_conn *conn = task->conn;
+ struct iscsi_session *sess = conn->session;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ struct cxgb3i_adapter *snic = cconn->hba->snic;
+ struct cxgb3i_tag_format *tformat = &snic->tag_format;
+ u32 sw_tag = (sess->age << cconn->task_idx_bits) | task->itt;
+ u32 tag;
+ int err = -EINVAL;
+
+ if (sc &&
+ (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_FROM_DEVICE) &&
+ cxgb3i_sw_tag_usable(tformat, sw_tag)) {
+ struct s3_conn *c3cn = cconn->cep->c3cn;
+ struct cxgb3i_gather_list *gl;
+
+ gl = cxgb3i_ddp_make_gl(scsi_in(sc)->length,
+ scsi_in(sc)->table.sgl,
+ scsi_in(sc)->table.nents,
+ snic->pdev,
+ GFP_ATOMIC);
+ if (gl) {
+ tag = sw_tag;
+ err = cxgb3i_ddp_tag_reserve(snic->tdev, c3cn->tid,
+ tformat, &tag,
+ gl, GFP_ATOMIC);
+ if (err < 0)
+ cxgb3i_ddp_release_gl(gl, snic->pdev);
+ }
+ }
+
+ if (err < 0)
+ tag = cxgb3i_set_non_ddp_tag(tformat, sw_tag);
+ /* the itt need to sent in big-endian order */
+ *hdr_itt = (__force itt_t)htonl(tag);
+
+ cxgb3i_tag_debug("new tag 0x%x/0x%x (itt 0x%x, age 0x%x).\n",
+ tag, *hdr_itt, task->itt, sess->age);
+ return 0;
+}
+
+/**
+ * cxgb3i_release_itt - release the tag for a given task
+ * if the tag is a ddp tag, release the ddp setup
+ * @task: iscsi task
+ * @hdr_itt: tag
+ */
+void cxgb3i_release_itt(struct iscsi_task *task, itt_t hdr_itt)
+{
+ struct scsi_cmnd *sc = task->sc;
+ struct iscsi_tcp_conn *tcp_conn = task->conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ struct cxgb3i_adapter *snic = cconn->hba->snic;
+ struct cxgb3i_tag_format *tformat = &snic->tag_format;
+ u32 tag = ntohl((__force u32)hdr_itt);
+
+ cxgb3i_tag_debug("release tag 0x%x.\n", tag);
+
+ if (sc &&
+ (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_FROM_DEVICE) &&
+ cxgb3i_is_ddp_tag(tformat, tag))
+ cxgb3i_ddp_tag_release(snic->tdev, tag);
+}
+
+/**
+ * cxgb3i_host_template -- Scsi_Host_Template structure
+ * used when registering with the scsi mid layer
+ */
+static struct scsi_host_template cxgb3i_host_template = {
+ .module = THIS_MODULE,
+ .name = "Chelsio S3xx iSCSI Initiator",
+ .proc_name = "cxgb3i",
+ .queuecommand = iscsi_queuecommand,
+ .change_queue_depth = iscsi_change_queue_depth,
+ .can_queue = 128 * (ISCSI_DEF_XMIT_CMDS_MAX - 1),
+ .sg_tablesize = SG_ALL,
+ .max_sectors = 0xFFFF,
+ .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN,
+ .eh_abort_handler = iscsi_eh_abort,
+ .eh_device_reset_handler = iscsi_eh_device_reset,
+ .eh_target_reset_handler = iscsi_eh_target_reset,
+ .use_clustering = DISABLE_CLUSTERING,
+ .this_id = -1,
+};
+
+static struct iscsi_transport cxgb3i_iscsi_transport = {
+ .owner = THIS_MODULE,
+ .name = "cxgb3i",
+ .caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
+ | CAP_DATADGST | CAP_DIGEST_OFFLOAD |
+ CAP_PADDING_OFFLOAD,
+ .param_mask = ISCSI_MAX_RECV_DLENGTH |
+ ISCSI_MAX_XMIT_DLENGTH |
+ ISCSI_HDRDGST_EN |
+ ISCSI_DATADGST_EN |
+ ISCSI_INITIAL_R2T_EN |
+ ISCSI_MAX_R2T |
+ ISCSI_IMM_DATA_EN |
+ ISCSI_FIRST_BURST |
+ ISCSI_MAX_BURST |
+ ISCSI_PDU_INORDER_EN |
+ ISCSI_DATASEQ_INORDER_EN |
+ ISCSI_ERL |
+ ISCSI_CONN_PORT |
+ ISCSI_CONN_ADDRESS |
+ ISCSI_EXP_STATSN |
+ ISCSI_PERSISTENT_PORT |
+ ISCSI_PERSISTENT_ADDRESS |
+ ISCSI_TARGET_NAME | ISCSI_TPGT |
+ ISCSI_USERNAME | ISCSI_PASSWORD |
+ ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
+ ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+ ISCSI_LU_RESET_TMO |
+ ISCSI_PING_TMO | ISCSI_RECV_TMO |
+ ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
+ .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
+ ISCSI_HOST_INITIATOR_NAME | ISCSI_HOST_NETDEV_NAME,
+ .get_host_param = cxgb3i_host_get_param,
+ .set_host_param = cxgb3i_host_set_param,
+ /* session management */
+ .create_session = cxgb3i_session_create,
+ .destroy_session = cxgb3i_session_destroy,
+ .get_session_param = iscsi_session_get_param,
+ /* connection management */
+ .create_conn = cxgb3i_conn_create,
+ .bind_conn = cxgb3i_conn_bind,
+ .destroy_conn = iscsi_tcp_conn_teardown,
+ .start_conn = iscsi_conn_start,
+ .stop_conn = iscsi_conn_stop,
+ .get_conn_param = cxgb3i_conn_get_param,
+ .set_param = cxgb3i_conn_set_param,
+ .get_stats = cxgb3i_conn_get_stats,
+ /* pdu xmit req. from user space */
+ .send_pdu = iscsi_conn_send_pdu,
+ /* task */
+ .init_task = iscsi_tcp_task_init,
+ .xmit_task = iscsi_tcp_task_xmit,
+ .cleanup_task = cxgb3i_conn_cleanup_task,
+
+ /* pdu */
+ .alloc_pdu = cxgb3i_conn_alloc_pdu,
+ .init_pdu = cxgb3i_conn_init_pdu,
+ .xmit_pdu = cxgb3i_conn_xmit_pdu,
+ .parse_pdu_itt = cxgb3i_parse_itt,
+
+ /* TCP connect/disconnect */
+ .ep_connect = cxgb3i_ep_connect,
+ .ep_poll = cxgb3i_ep_poll,
+ .ep_disconnect = cxgb3i_ep_disconnect,
+ /* Error recovery timeout call */
+ .session_recovery_timedout = iscsi_session_recovery_timedout,
+};
+
+int cxgb3i_iscsi_init(void)
+{
+ sw_tag_idx_bits = (__ilog2_u32(ISCSI_ITT_MASK)) + 1;
+ sw_tag_age_bits = (__ilog2_u32(ISCSI_AGE_MASK)) + 1;
+ cxgb3i_log_info("tag itt 0x%x, %u bits, age 0x%x, %u bits.\n",
+ ISCSI_ITT_MASK, sw_tag_idx_bits,
+ ISCSI_AGE_MASK, sw_tag_age_bits);
+
+ cxgb3i_scsi_transport =
+ iscsi_register_transport(&cxgb3i_iscsi_transport);
+ if (!cxgb3i_scsi_transport) {
+ cxgb3i_log_error("Could not register cxgb3i transport.\n");
+ return -ENODEV;
+ }
+ cxgb3i_api_debug("cxgb3i transport 0x%p.\n", cxgb3i_scsi_transport);
+ return 0;
+}
+
+void cxgb3i_iscsi_cleanup(void)
+{
+ if (cxgb3i_scsi_transport) {
+ cxgb3i_api_debug("cxgb3i transport 0x%p.\n",
+ cxgb3i_scsi_transport);
+ iscsi_unregister_transport(&cxgb3i_iscsi_transport);
+ }
+}
diff --git a/drivers/scsi/cxgb3i/cxgb3i_offload.c b/drivers/scsi/cxgb3i/cxgb3i_offload.c
new file mode 100644
index 000000000000..a865f1fefe8b
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_offload.c
@@ -0,0 +1,1810 @@
+/*
+ * cxgb3i_offload.c: Chelsio S3xx iscsi offloaded tcp connection management
+ *
+ * Copyright (C) 2003-2008 Chelsio Communications. All rights reserved.
+ *
+ * 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 LICENSE file included in this
+ * release for licensing terms and conditions.
+ *
+ * Written by: Dimitris Michailidis (dm@chelsio.com)
+ * Karen Xie (kxie@chelsio.com)
+ */
+
+#include <linux/if_vlan.h>
+#include <linux/version.h>
+
+#include "cxgb3_defs.h"
+#include "cxgb3_ctl_defs.h"
+#include "firmware_exports.h"
+#include "cxgb3i_offload.h"
+#include "cxgb3i_pdu.h"
+#include "cxgb3i_ddp.h"
+
+#ifdef __DEBUG_C3CN_CONN__
+#define c3cn_conn_debug cxgb3i_log_info
+#else
+#define c3cn_conn_debug(fmt...)
+#endif
+
+#ifdef __DEBUG_C3CN_TX__
+#define c3cn_tx_debug cxgb3i_log_debug
+#else
+#define c3cn_tx_debug(fmt...)
+#endif
+
+#ifdef __DEBUG_C3CN_RX__
+#define c3cn_rx_debug cxgb3i_log_debug
+#else
+#define c3cn_rx_debug(fmt...)
+#endif
+
+/*
+ * module parameters releated to offloaded iscsi connection
+ */
+static int cxgb3_rcv_win = 256 * 1024;
+module_param(cxgb3_rcv_win, int, 0644);
+MODULE_PARM_DESC(cxgb3_rcv_win, "TCP receive window in bytes (default=256KB)");
+
+static int cxgb3_snd_win = 64 * 1024;
+module_param(cxgb3_snd_win, int, 0644);
+MODULE_PARM_DESC(cxgb3_snd_win, "TCP send window in bytes (default=64KB)");
+
+static int cxgb3_rx_credit_thres = 10 * 1024;
+module_param(cxgb3_rx_credit_thres, int, 0644);
+MODULE_PARM_DESC(rx_credit_thres,
+ "RX credits return threshold in bytes (default=10KB)");
+
+static unsigned int cxgb3_max_connect = 8 * 1024;
+module_param(cxgb3_max_connect, uint, 0644);
+MODULE_PARM_DESC(cxgb3_max_connect, "Max. # of connections (default=8092)");
+
+static unsigned int cxgb3_sport_base = 20000;
+module_param(cxgb3_sport_base, uint, 0644);
+MODULE_PARM_DESC(cxgb3_sport_base, "starting port number (default=20000)");
+
+/*
+ * cxgb3i tcp connection data(per adapter) list
+ */
+static LIST_HEAD(cdata_list);
+static DEFINE_RWLOCK(cdata_rwlock);
+
+static int c3cn_push_tx_frames(struct s3_conn *c3cn, int req_completion);
+static void c3cn_release_offload_resources(struct s3_conn *c3cn);
+
+/*
+ * iscsi source port management
+ *
+ * Find a free source port in the port allocation map. We use a very simple
+ * rotor scheme to look for the next free port.
+ *
+ * If a source port has been specified make sure that it doesn't collide with
+ * our normal source port allocation map. If it's outside the range of our
+ * allocation/deallocation scheme just let them use it.
+ *
+ * If the source port is outside our allocation range, the caller is
+ * responsible for keeping track of their port usage.
+ */
+static int c3cn_get_port(struct s3_conn *c3cn, struct cxgb3i_sdev_data *cdata)
+{
+ unsigned int start;
+ int idx;
+
+ if (!cdata)
+ goto error_out;
+
+ if (c3cn->saddr.sin_port != 0) {
+ idx = ntohs(c3cn->saddr.sin_port) - cxgb3_sport_base;
+ if (idx < 0 || idx >= cxgb3_max_connect)
+ return 0;
+ if (!test_and_set_bit(idx, cdata->sport_map))
+ return -EADDRINUSE;
+ }
+
+ /* the sport_map_next may not be accurate but that is okay, sport_map
+ should be */
+ start = idx = cdata->sport_map_next;
+ do {
+ if (++idx >= cxgb3_max_connect)
+ idx = 0;
+ if (!(test_and_set_bit(idx, cdata->sport_map))) {
+ c3cn->saddr.sin_port = htons(cxgb3_sport_base + idx);
+ cdata->sport_map_next = idx;
+ c3cn_conn_debug("%s reserve port %u.\n",
+ cdata->cdev->name,
+ cxgb3_sport_base + idx);
+ return 0;
+ }
+ } while (idx != start);
+
+error_out:
+ return -EADDRNOTAVAIL;
+}
+
+static void c3cn_put_port(struct s3_conn *c3cn)
+{
+ struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(c3cn->cdev);
+
+ if (c3cn->saddr.sin_port) {
+ int idx = ntohs(c3cn->saddr.sin_port) - cxgb3_sport_base;
+
+ c3cn->saddr.sin_port = 0;
+ if (idx < 0 || idx >= cxgb3_max_connect)
+ return;
+ clear_bit(idx, cdata->sport_map);
+ c3cn_conn_debug("%s, release port %u.\n",
+ cdata->cdev->name, cxgb3_sport_base + idx);
+ }
+}
+
+static inline void c3cn_set_flag(struct s3_conn *c3cn, enum c3cn_flags flag)
+{
+ __set_bit(flag, &c3cn->flags);
+ c3cn_conn_debug("c3cn 0x%p, set %d, s %u, f 0x%lx.\n",
+ c3cn, flag, c3cn->state, c3cn->flags);
+}
+
+static inline void c3cn_clear_flag(struct s3_conn *c3cn, enum c3cn_flags flag)
+{
+ __clear_bit(flag, &c3cn->flags);
+ c3cn_conn_debug("c3cn 0x%p, clear %d, s %u, f 0x%lx.\n",
+ c3cn, flag, c3cn->state, c3cn->flags);
+}
+
+static inline int c3cn_flag(struct s3_conn *c3cn, enum c3cn_flags flag)
+{
+ if (c3cn == NULL)
+ return 0;
+ return test_bit(flag, &c3cn->flags);
+}
+
+static void c3cn_set_state(struct s3_conn *c3cn, int state)
+{
+ c3cn_conn_debug("c3cn 0x%p state -> %u.\n", c3cn, state);
+ c3cn->state = state;
+}
+
+static inline void c3cn_hold(struct s3_conn *c3cn)
+{
+ atomic_inc(&c3cn->refcnt);
+}
+
+static inline void c3cn_put(struct s3_conn *c3cn)
+{
+ if (atomic_dec_and_test(&c3cn->refcnt)) {
+ c3cn_conn_debug("free c3cn 0x%p, s %u, f 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+ kfree(c3cn);
+ }
+}
+
+static void c3cn_closed(struct s3_conn *c3cn)
+{
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ c3cn_put_port(c3cn);
+ c3cn_release_offload_resources(c3cn);
+ c3cn_set_state(c3cn, C3CN_STATE_CLOSED);
+ cxgb3i_conn_closing(c3cn);
+}
+
+/*
+ * CPL (Chelsio Protocol Language) defines a message passing interface between
+ * the host driver and T3 asic.
+ * The section below implments CPLs that related to iscsi tcp connection
+ * open/close/abort and data send/receive.
+ */
+
+/*
+ * CPL connection active open request: host ->
+ */
+static unsigned int find_best_mtu(const struct t3c_data *d, unsigned short mtu)
+{
+ int i = 0;
+
+ while (i < d->nmtus - 1 && d->mtus[i + 1] <= mtu)
+ ++i;
+ return i;
+}
+
+static unsigned int select_mss(struct s3_conn *c3cn, unsigned int pmtu)
+{
+ unsigned int idx;
+ struct dst_entry *dst = c3cn->dst_cache;
+ struct t3cdev *cdev = c3cn->cdev;
+ const struct t3c_data *td = T3C_DATA(cdev);
+ u16 advmss = dst_metric(dst, RTAX_ADVMSS);
+
+ if (advmss > pmtu - 40)
+ advmss = pmtu - 40;
+ if (advmss < td->mtus[0] - 40)
+ advmss = td->mtus[0] - 40;
+ idx = find_best_mtu(td, advmss + 40);
+ return idx;
+}
+
+static inline int compute_wscale(int win)
+{
+ int wscale = 0;
+ while (wscale < 14 && (65535<<wscale) < win)
+ wscale++;
+ return wscale;
+}
+
+static inline unsigned int calc_opt0h(struct s3_conn *c3cn)
+{
+ int wscale = compute_wscale(cxgb3_rcv_win);
+ return V_KEEP_ALIVE(1) |
+ F_TCAM_BYPASS |
+ V_WND_SCALE(wscale) |
+ V_MSS_IDX(c3cn->mss_idx);
+}
+
+static inline unsigned int calc_opt0l(struct s3_conn *c3cn)
+{
+ return V_ULP_MODE(ULP_MODE_ISCSI) |
+ V_RCV_BUFSIZ(cxgb3_rcv_win>>10);
+}
+
+static void make_act_open_req(struct s3_conn *c3cn, struct sk_buff *skb,
+ unsigned int atid, const struct l2t_entry *e)
+{
+ struct cpl_act_open_req *req;
+
+ c3cn_conn_debug("c3cn 0x%p, atid 0x%x.\n", c3cn, atid);
+
+ skb->priority = CPL_PRIORITY_SETUP;
+ req = (struct cpl_act_open_req *)__skb_put(skb, sizeof(*req));
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_ACT_OPEN_REQ, atid));
+ req->local_port = c3cn->saddr.sin_port;
+ req->peer_port = c3cn->daddr.sin_port;
+ req->local_ip = c3cn->saddr.sin_addr.s_addr;
+ req->peer_ip = c3cn->daddr.sin_addr.s_addr;
+ req->opt0h = htonl(calc_opt0h(c3cn) | V_L2T_IDX(e->idx) |
+ V_TX_CHANNEL(e->smt_idx));
+ req->opt0l = htonl(calc_opt0l(c3cn));
+ req->params = 0;
+}
+
+static void fail_act_open(struct s3_conn *c3cn, int errno)
+{
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+ c3cn->err = errno;
+ c3cn_closed(c3cn);
+}
+
+static void act_open_req_arp_failure(struct t3cdev *dev, struct sk_buff *skb)
+{
+ struct s3_conn *c3cn = (struct s3_conn *)skb->sk;
+
+ c3cn_conn_debug("c3cn 0x%p, state %u.\n", c3cn, c3cn->state);
+
+ c3cn_hold(c3cn);
+ spin_lock_bh(&c3cn->lock);
+ if (c3cn->state == C3CN_STATE_CONNECTING)
+ fail_act_open(c3cn, EHOSTUNREACH);
+ spin_unlock_bh(&c3cn->lock);
+ c3cn_put(c3cn);
+ __kfree_skb(skb);
+}
+
+/*
+ * CPL connection close request: host ->
+ *
+ * Close a connection by sending a CPL_CLOSE_CON_REQ message and queue it to
+ * the write queue (i.e., after any unsent txt data).
+ */
+static void skb_entail(struct s3_conn *c3cn, struct sk_buff *skb,
+ int flags)
+{
+ CXGB3_SKB_CB(skb)->seq = c3cn->write_seq;
+ CXGB3_SKB_CB(skb)->flags = flags;
+ __skb_queue_tail(&c3cn->write_queue, skb);
+}
+
+static void send_close_req(struct s3_conn *c3cn)
+{
+ struct sk_buff *skb = c3cn->cpl_close;
+ struct cpl_close_con_req *req = (struct cpl_close_con_req *)skb->head;
+ unsigned int tid = c3cn->tid;
+
+ c3cn_conn_debug("c3cn 0x%p, state 0x%x, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ c3cn->cpl_close = NULL;
+
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_CLOSE_CON));
+ req->wr.wr_lo = htonl(V_WR_TID(tid));
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_CON_REQ, tid));
+ req->rsvd = htonl(c3cn->write_seq);
+
+ skb_entail(c3cn, skb, C3CB_FLAG_NO_APPEND);
+ if (c3cn->state != C3CN_STATE_CONNECTING)
+ c3cn_push_tx_frames(c3cn, 1);
+}
+
+/*
+ * CPL connection abort request: host ->
+ *
+ * Send an ABORT_REQ message. Makes sure we do not send multiple ABORT_REQs
+ * for the same connection and also that we do not try to send a message
+ * after the connection has closed.
+ */
+static void abort_arp_failure(struct t3cdev *cdev, struct sk_buff *skb)
+{
+ struct cpl_abort_req *req = cplhdr(skb);
+
+ c3cn_conn_debug("tdev 0x%p.\n", cdev);
+
+ req->cmd = CPL_ABORT_NO_RST;
+ cxgb3_ofld_send(cdev, skb);
+}
+
+static inline void c3cn_purge_write_queue(struct s3_conn *c3cn)
+{
+ struct sk_buff *skb;
+
+ while ((skb = __skb_dequeue(&c3cn->write_queue)))
+ __kfree_skb(skb);
+}
+
+static void send_abort_req(struct s3_conn *c3cn)
+{
+ struct sk_buff *skb = c3cn->cpl_abort_req;
+ struct cpl_abort_req *req;
+ unsigned int tid = c3cn->tid;
+
+ if (unlikely(c3cn->state == C3CN_STATE_ABORTING) || !skb ||
+ !c3cn->cdev)
+ return;
+
+ c3cn_set_state(c3cn, C3CN_STATE_ABORTING);
+
+ c3cn_conn_debug("c3cn 0x%p, flag ABORT_RPL + ABORT_SHUT.\n", c3cn);
+
+ c3cn_set_flag(c3cn, C3CN_ABORT_RPL_PENDING);
+
+ /* Purge the send queue so we don't send anything after an abort. */
+ c3cn_purge_write_queue(c3cn);
+
+ c3cn->cpl_abort_req = NULL;
+ req = (struct cpl_abort_req *)skb->head;
+
+ skb->priority = CPL_PRIORITY_DATA;
+ set_arp_failure_handler(skb, abort_arp_failure);
+
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_REQ));
+ req->wr.wr_lo = htonl(V_WR_TID(tid));
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_ABORT_REQ, tid));
+ req->rsvd0 = htonl(c3cn->snd_nxt);
+ req->rsvd1 = !c3cn_flag(c3cn, C3CN_TX_DATA_SENT);
+ req->cmd = CPL_ABORT_SEND_RST;
+
+ l2t_send(c3cn->cdev, skb, c3cn->l2t);
+}
+
+/*
+ * CPL connection abort reply: host ->
+ *
+ * Send an ABORT_RPL message in response of the ABORT_REQ received.
+ */
+static void send_abort_rpl(struct s3_conn *c3cn, int rst_status)
+{
+ struct sk_buff *skb = c3cn->cpl_abort_rpl;
+ struct cpl_abort_rpl *rpl = (struct cpl_abort_rpl *)skb->head;
+
+ c3cn->cpl_abort_rpl = NULL;
+
+ skb->priority = CPL_PRIORITY_DATA;
+ rpl->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_RPL));
+ rpl->wr.wr_lo = htonl(V_WR_TID(c3cn->tid));
+ OPCODE_TID(rpl) = htonl(MK_OPCODE_TID(CPL_ABORT_RPL, c3cn->tid));
+ rpl->cmd = rst_status;
+
+ cxgb3_ofld_send(c3cn->cdev, skb);
+}
+
+/*
+ * CPL connection rx data ack: host ->
+ * Send RX credits through an RX_DATA_ACK CPL message. Returns the number of
+ * credits sent.
+ */
+static u32 send_rx_credits(struct s3_conn *c3cn, u32 credits, u32 dack)
+{
+ struct sk_buff *skb;
+ struct cpl_rx_data_ack *req;
+
+ skb = alloc_skb(sizeof(*req), GFP_ATOMIC);
+ if (!skb)
+ return 0;
+
+ req = (struct cpl_rx_data_ack *)__skb_put(skb, sizeof(*req));
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_RX_DATA_ACK, c3cn->tid));
+ req->credit_dack = htonl(dack | V_RX_CREDITS(credits));
+ skb->priority = CPL_PRIORITY_ACK;
+ cxgb3_ofld_send(c3cn->cdev, skb);
+ return credits;
+}
+
+/*
+ * CPL connection tx data: host ->
+ *
+ * Send iscsi PDU via TX_DATA CPL message. Returns the number of
+ * credits sent.
+ * Each TX_DATA consumes work request credit (wrs), so we need to keep track of
+ * how many we've used so far and how many are pending (i.e., yet ack'ed by T3).
+ */
+
+/*
+ * For ULP connections HW may inserts digest bytes into the pdu. Those digest
+ * bytes are not sent by the host but are part of the TCP payload and therefore
+ * consume TCP sequence space.
+ */
+static const unsigned int cxgb3_ulp_extra_len[] = { 0, 4, 4, 8 };
+static inline unsigned int ulp_extra_len(const struct sk_buff *skb)
+{
+ return cxgb3_ulp_extra_len[skb_ulp_mode(skb) & 3];
+}
+
+static unsigned int wrlen __read_mostly;
+
+/*
+ * The number of WRs needed for an skb depends on the number of fragments
+ * in the skb and whether it has any payload in its main body. This maps the
+ * length of the gather list represented by an skb into the # of necessary WRs.
+ *
+ * The max. length of an skb is controlled by the max pdu size which is ~16K.
+ * Also, assume the min. fragment length is the sector size (512), then add
+ * extra fragment counts for iscsi bhs and payload padding.
+ */
+#define SKB_WR_LIST_SIZE (16384/512 + 3)
+static unsigned int skb_wrs[SKB_WR_LIST_SIZE] __read_mostly;
+
+static void s3_init_wr_tab(unsigned int wr_len)
+{
+ int i;
+
+ if (skb_wrs[1]) /* already initialized */
+ return;
+
+ for (i = 1; i < SKB_WR_LIST_SIZE; i++) {
+ int sgl_len = (3 * i) / 2 + (i & 1);
+
+ sgl_len += 3;
+ skb_wrs[i] = (sgl_len <= wr_len
+ ? 1 : 1 + (sgl_len - 2) / (wr_len - 1));
+ }
+
+ wrlen = wr_len * 8;
+}
+
+static inline void reset_wr_list(struct s3_conn *c3cn)
+{
+ c3cn->wr_pending_head = NULL;
+}
+
+/*
+ * Add a WR to a connections's list of pending WRs. This is a singly-linked
+ * list of sk_buffs operating as a FIFO. The head is kept in wr_pending_head
+ * and the tail in wr_pending_tail.
+ */
+static inline void enqueue_wr(struct s3_conn *c3cn,
+ struct sk_buff *skb)
+{
+ skb_wr_data(skb) = NULL;
+
+ /*
+ * We want to take an extra reference since both us and the driver
+ * need to free the packet before it's really freed. We know there's
+ * just one user currently so we use atomic_set rather than skb_get
+ * to avoid the atomic op.
+ */
+ atomic_set(&skb->users, 2);
+
+ if (!c3cn->wr_pending_head)
+ c3cn->wr_pending_head = skb;
+ else
+ skb_wr_data(skb) = skb;
+ c3cn->wr_pending_tail = skb;
+}
+
+static inline struct sk_buff *peek_wr(const struct s3_conn *c3cn)
+{
+ return c3cn->wr_pending_head;
+}
+
+static inline void free_wr_skb(struct sk_buff *skb)
+{
+ kfree_skb(skb);
+}
+
+static inline struct sk_buff *dequeue_wr(struct s3_conn *c3cn)
+{
+ struct sk_buff *skb = c3cn->wr_pending_head;
+
+ if (likely(skb)) {
+ /* Don't bother clearing the tail */
+ c3cn->wr_pending_head = skb_wr_data(skb);
+ skb_wr_data(skb) = NULL;
+ }
+ return skb;
+}
+
+static void purge_wr_queue(struct s3_conn *c3cn)
+{
+ struct sk_buff *skb;
+ while ((skb = dequeue_wr(c3cn)) != NULL)
+ free_wr_skb(skb);
+}
+
+static inline void make_tx_data_wr(struct s3_conn *c3cn, struct sk_buff *skb,
+ int len)
+{
+ struct tx_data_wr *req;
+
+ skb_reset_transport_header(skb);
+ req = (struct tx_data_wr *)__skb_push(skb, sizeof(*req));
+ req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA));
+ req->wr_lo = htonl(V_WR_TID(c3cn->tid));
+ req->sndseq = htonl(c3cn->snd_nxt);
+ /* len includes the length of any HW ULP additions */
+ req->len = htonl(len);
+ req->param = htonl(V_TX_PORT(c3cn->l2t->smt_idx));
+ /* V_TX_ULP_SUBMODE sets both the mode and submode */
+ req->flags = htonl(V_TX_ULP_SUBMODE(skb_ulp_mode(skb)) |
+ V_TX_SHOVE((skb_peek(&c3cn->write_queue) ? 0 : 1)));
+
+ if (!c3cn_flag(c3cn, C3CN_TX_DATA_SENT)) {
+ req->flags |= htonl(V_TX_ACK_PAGES(2) | F_TX_INIT |
+ V_TX_CPU_IDX(c3cn->qset));
+ /* Sendbuffer is in units of 32KB. */
+ req->param |= htonl(V_TX_SNDBUF(cxgb3_snd_win >> 15));
+ c3cn_set_flag(c3cn, C3CN_TX_DATA_SENT);
+ }
+}
+
+/**
+ * c3cn_push_tx_frames -- start transmit
+ * @c3cn: the offloaded connection
+ * @req_completion: request wr_ack or not
+ *
+ * Prepends TX_DATA_WR or CPL_CLOSE_CON_REQ headers to buffers waiting in a
+ * connection's send queue and sends them on to T3. Must be called with the
+ * connection's lock held. Returns the amount of send buffer space that was
+ * freed as a result of sending queued data to T3.
+ */
+static void arp_failure_discard(struct t3cdev *cdev, struct sk_buff *skb)
+{
+ kfree_skb(skb);
+}
+
+static int c3cn_push_tx_frames(struct s3_conn *c3cn, int req_completion)
+{
+ int total_size = 0;
+ struct sk_buff *skb;
+ struct t3cdev *cdev;
+ struct cxgb3i_sdev_data *cdata;
+
+ if (unlikely(c3cn->state == C3CN_STATE_CONNECTING ||
+ c3cn->state == C3CN_STATE_CLOSE_WAIT_1 ||
+ c3cn->state == C3CN_STATE_ABORTING)) {
+ c3cn_tx_debug("c3cn 0x%p, in closing state %u.\n",
+ c3cn, c3cn->state);
+ return 0;
+ }
+
+ cdev = c3cn->cdev;
+ cdata = CXGB3_SDEV_DATA(cdev);
+
+ while (c3cn->wr_avail
+ && (skb = skb_peek(&c3cn->write_queue)) != NULL) {
+ int len = skb->len; /* length before skb_push */
+ int frags = skb_shinfo(skb)->nr_frags + (len != skb->data_len);
+ int wrs_needed = skb_wrs[frags];
+
+ if (wrs_needed > 1 && len + sizeof(struct tx_data_wr) <= wrlen)
+ wrs_needed = 1;
+
+ WARN_ON(frags >= SKB_WR_LIST_SIZE || wrs_needed < 1);
+
+ if (c3cn->wr_avail < wrs_needed) {
+ c3cn_tx_debug("c3cn 0x%p, skb len %u/%u, frag %u, "
+ "wr %d < %u.\n",
+ c3cn, skb->len, skb->datalen, frags,
+ wrs_needed, c3cn->wr_avail);
+ break;
+ }
+
+ __skb_unlink(skb, &c3cn->write_queue);
+ skb->priority = CPL_PRIORITY_DATA;
+ skb->csum = wrs_needed; /* remember this until the WR_ACK */
+ c3cn->wr_avail -= wrs_needed;
+ c3cn->wr_unacked += wrs_needed;
+ enqueue_wr(c3cn, skb);
+
+ if (likely(CXGB3_SKB_CB(skb)->flags & C3CB_FLAG_NEED_HDR)) {
+ len += ulp_extra_len(skb);
+ make_tx_data_wr(c3cn, skb, len);
+ c3cn->snd_nxt += len;
+ if ((req_completion
+ && c3cn->wr_unacked == wrs_needed)
+ || (CXGB3_SKB_CB(skb)->flags & C3CB_FLAG_COMPL)
+ || c3cn->wr_unacked >= c3cn->wr_max / 2) {
+ struct work_request_hdr *wr = cplhdr(skb);
+
+ wr->wr_hi |= htonl(F_WR_COMPL);
+ c3cn->wr_unacked = 0;
+ }
+ CXGB3_SKB_CB(skb)->flags &= ~C3CB_FLAG_NEED_HDR;
+ }
+
+ total_size += skb->truesize;
+ set_arp_failure_handler(skb, arp_failure_discard);
+ l2t_send(cdev, skb, c3cn->l2t);
+ }
+ return total_size;
+}
+
+/*
+ * process_cpl_msg: -> host
+ * Top-level CPL message processing used by most CPL messages that
+ * pertain to connections.
+ */
+static inline void process_cpl_msg(void (*fn)(struct s3_conn *,
+ struct sk_buff *),
+ struct s3_conn *c3cn,
+ struct sk_buff *skb)
+{
+ spin_lock_bh(&c3cn->lock);
+ fn(c3cn, skb);
+ spin_unlock_bh(&c3cn->lock);
+}
+
+/*
+ * process_cpl_msg_ref: -> host
+ * Similar to process_cpl_msg() but takes an extra connection reference around
+ * the call to the handler. Should be used if the handler may drop a
+ * connection reference.
+ */
+static inline void process_cpl_msg_ref(void (*fn) (struct s3_conn *,
+ struct sk_buff *),
+ struct s3_conn *c3cn,
+ struct sk_buff *skb)
+{
+ c3cn_hold(c3cn);
+ process_cpl_msg(fn, c3cn, skb);
+ c3cn_put(c3cn);
+}
+
+/*
+ * Process a CPL_ACT_ESTABLISH message: -> host
+ * Updates connection state from an active establish CPL message. Runs with
+ * the connection lock held.
+ */
+
+static inline void s3_free_atid(struct t3cdev *cdev, unsigned int tid)
+{
+ struct s3_conn *c3cn = cxgb3_free_atid(cdev, tid);
+ if (c3cn)
+ c3cn_put(c3cn);
+}
+
+static void c3cn_established(struct s3_conn *c3cn, u32 snd_isn,
+ unsigned int opt)
+{
+ c3cn_conn_debug("c3cn 0x%p, state %u.\n", c3cn, c3cn->state);
+
+ c3cn->write_seq = c3cn->snd_nxt = c3cn->snd_una = snd_isn;
+
+ /*
+ * Causes the first RX_DATA_ACK to supply any Rx credits we couldn't
+ * pass through opt0.
+ */
+ if (cxgb3_rcv_win > (M_RCV_BUFSIZ << 10))
+ c3cn->rcv_wup -= cxgb3_rcv_win - (M_RCV_BUFSIZ << 10);
+
+ dst_confirm(c3cn->dst_cache);
+
+ smp_mb();
+
+ c3cn_set_state(c3cn, C3CN_STATE_ESTABLISHED);
+}
+
+static void process_act_establish(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ struct cpl_act_establish *req = cplhdr(skb);
+ u32 rcv_isn = ntohl(req->rcv_isn); /* real RCV_ISN + 1 */
+
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ if (unlikely(c3cn->state != C3CN_STATE_CONNECTING))
+ cxgb3i_log_error("TID %u expected SYN_SENT, got EST., s %u\n",
+ c3cn->tid, c3cn->state);
+
+ c3cn->copied_seq = c3cn->rcv_wup = c3cn->rcv_nxt = rcv_isn;
+ c3cn_established(c3cn, ntohl(req->snd_isn), ntohs(req->tcp_opt));
+
+ __kfree_skb(skb);
+
+ if (unlikely(c3cn_flag(c3cn, C3CN_ACTIVE_CLOSE_NEEDED)))
+ /* upper layer has requested closing */
+ send_abort_req(c3cn);
+ else if (c3cn_push_tx_frames(c3cn, 1))
+ cxgb3i_conn_tx_open(c3cn);
+}
+
+static int do_act_establish(struct t3cdev *cdev, struct sk_buff *skb,
+ void *ctx)
+{
+ struct cpl_act_establish *req = cplhdr(skb);
+ unsigned int tid = GET_TID(req);
+ unsigned int atid = G_PASS_OPEN_TID(ntohl(req->tos_tid));
+ struct s3_conn *c3cn = ctx;
+ struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(cdev);
+
+ c3cn_conn_debug("rcv, tid 0x%x, c3cn 0x%p, s %u, f 0x%lx.\n",
+ tid, c3cn, c3cn->state, c3cn->flags);
+
+ c3cn->tid = tid;
+ c3cn_hold(c3cn);
+ cxgb3_insert_tid(cdata->cdev, cdata->client, c3cn, tid);
+ s3_free_atid(cdev, atid);
+
+ c3cn->qset = G_QNUM(ntohl(skb->csum));
+
+ process_cpl_msg(process_act_establish, c3cn, skb);
+ return 0;
+}
+
+/*
+ * Process a CPL_ACT_OPEN_RPL message: -> host
+ * Handle active open failures.
+ */
+static int act_open_rpl_status_to_errno(int status)
+{
+ switch (status) {
+ case CPL_ERR_CONN_RESET:
+ return ECONNREFUSED;
+ case CPL_ERR_ARP_MISS:
+ return EHOSTUNREACH;
+ case CPL_ERR_CONN_TIMEDOUT:
+ return ETIMEDOUT;
+ case CPL_ERR_TCAM_FULL:
+ return ENOMEM;
+ case CPL_ERR_CONN_EXIST:
+ cxgb3i_log_error("ACTIVE_OPEN_RPL: 4-tuple in use\n");
+ return EADDRINUSE;
+ default:
+ return EIO;
+ }
+}
+
+static void act_open_retry_timer(unsigned long data)
+{
+ struct sk_buff *skb;
+ struct s3_conn *c3cn = (struct s3_conn *)data;
+
+ c3cn_conn_debug("c3cn 0x%p, state %u.\n", c3cn, c3cn->state);
+
+ spin_lock_bh(&c3cn->lock);
+ skb = alloc_skb(sizeof(struct cpl_act_open_req), GFP_ATOMIC);
+ if (!skb)
+ fail_act_open(c3cn, ENOMEM);
+ else {
+ skb->sk = (struct sock *)c3cn;
+ set_arp_failure_handler(skb, act_open_req_arp_failure);
+ make_act_open_req(c3cn, skb, c3cn->tid, c3cn->l2t);
+ l2t_send(c3cn->cdev, skb, c3cn->l2t);
+ }
+ spin_unlock_bh(&c3cn->lock);
+ c3cn_put(c3cn);
+}
+
+static void process_act_open_rpl(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ struct cpl_act_open_rpl *rpl = cplhdr(skb);
+
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ if (rpl->status == CPL_ERR_CONN_EXIST &&
+ c3cn->retry_timer.function != act_open_retry_timer) {
+ c3cn->retry_timer.function = act_open_retry_timer;
+ if (!mod_timer(&c3cn->retry_timer, jiffies + HZ / 2))
+ c3cn_hold(c3cn);
+ } else
+ fail_act_open(c3cn, act_open_rpl_status_to_errno(rpl->status));
+ __kfree_skb(skb);
+}
+
+static int do_act_open_rpl(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
+{
+ struct s3_conn *c3cn = ctx;
+ struct cpl_act_open_rpl *rpl = cplhdr(skb);
+
+ c3cn_conn_debug("rcv, status 0x%x, c3cn 0x%p, s %u, f 0x%lx.\n",
+ rpl->status, c3cn, c3cn->state, c3cn->flags);
+
+ if (rpl->status != CPL_ERR_TCAM_FULL &&
+ rpl->status != CPL_ERR_CONN_EXIST &&
+ rpl->status != CPL_ERR_ARP_MISS)
+ cxgb3_queue_tid_release(cdev, GET_TID(rpl));
+
+ process_cpl_msg_ref(process_act_open_rpl, c3cn, skb);
+ return 0;
+}
+
+/*
+ * Process PEER_CLOSE CPL messages: -> host
+ * Handle peer FIN.
+ */
+static void process_peer_close(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ if (c3cn_flag(c3cn, C3CN_ABORT_RPL_PENDING))
+ goto out;
+
+ switch (c3cn->state) {
+ case C3CN_STATE_ESTABLISHED:
+ c3cn_set_state(c3cn, C3CN_STATE_PASSIVE_CLOSE);
+ break;
+ case C3CN_STATE_ACTIVE_CLOSE:
+ c3cn_set_state(c3cn, C3CN_STATE_CLOSE_WAIT_2);
+ break;
+ case C3CN_STATE_CLOSE_WAIT_1:
+ c3cn_closed(c3cn);
+ break;
+ case C3CN_STATE_ABORTING:
+ break;
+ default:
+ cxgb3i_log_error("%s: peer close, TID %u in bad state %u\n",
+ c3cn->cdev->name, c3cn->tid, c3cn->state);
+ }
+
+ cxgb3i_conn_closing(c3cn);
+out:
+ __kfree_skb(skb);
+}
+
+static int do_peer_close(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
+{
+ struct s3_conn *c3cn = ctx;
+
+ c3cn_conn_debug("rcv, c3cn 0x%p, s %u, f 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+ process_cpl_msg_ref(process_peer_close, c3cn, skb);
+ return 0;
+}
+
+/*
+ * Process CLOSE_CONN_RPL CPL message: -> host
+ * Process a peer ACK to our FIN.
+ */
+static void process_close_con_rpl(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ struct cpl_close_con_rpl *rpl = cplhdr(skb);
+
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ c3cn->snd_una = ntohl(rpl->snd_nxt) - 1; /* exclude FIN */
+
+ if (c3cn_flag(c3cn, C3CN_ABORT_RPL_PENDING))
+ goto out;
+
+ switch (c3cn->state) {
+ case C3CN_STATE_ACTIVE_CLOSE:
+ c3cn_set_state(c3cn, C3CN_STATE_CLOSE_WAIT_1);
+ break;
+ case C3CN_STATE_CLOSE_WAIT_1:
+ case C3CN_STATE_CLOSE_WAIT_2:
+ c3cn_closed(c3cn);
+ break;
+ case C3CN_STATE_ABORTING:
+ break;
+ default:
+ cxgb3i_log_error("%s: close_rpl, TID %u in bad state %u\n",
+ c3cn->cdev->name, c3cn->tid, c3cn->state);
+ }
+
+out:
+ kfree_skb(skb);
+}
+
+static int do_close_con_rpl(struct t3cdev *cdev, struct sk_buff *skb,
+ void *ctx)
+{
+ struct s3_conn *c3cn = ctx;
+
+ c3cn_conn_debug("rcv, c3cn 0x%p, s %u, f 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ process_cpl_msg_ref(process_close_con_rpl, c3cn, skb);
+ return 0;
+}
+
+/*
+ * Process ABORT_REQ_RSS CPL message: -> host
+ * Process abort requests. If we are waiting for an ABORT_RPL we ignore this
+ * request except that we need to reply to it.
+ */
+
+static int abort_status_to_errno(struct s3_conn *c3cn, int abort_reason,
+ int *need_rst)
+{
+ switch (abort_reason) {
+ case CPL_ERR_BAD_SYN: /* fall through */
+ case CPL_ERR_CONN_RESET:
+ return c3cn->state > C3CN_STATE_ESTABLISHED ?
+ EPIPE : ECONNRESET;
+ case CPL_ERR_XMIT_TIMEDOUT:
+ case CPL_ERR_PERSIST_TIMEDOUT:
+ case CPL_ERR_FINWAIT2_TIMEDOUT:
+ case CPL_ERR_KEEPALIVE_TIMEDOUT:
+ return ETIMEDOUT;
+ default:
+ return EIO;
+ }
+}
+
+static void process_abort_req(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ int rst_status = CPL_ABORT_NO_RST;
+ const struct cpl_abort_req_rss *req = cplhdr(skb);
+
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ if (!c3cn_flag(c3cn, C3CN_ABORT_REQ_RCVD)) {
+ c3cn_set_flag(c3cn, C3CN_ABORT_REQ_RCVD);
+ c3cn_set_state(c3cn, C3CN_STATE_ABORTING);
+ __kfree_skb(skb);
+ return;
+ }
+
+ c3cn_clear_flag(c3cn, C3CN_ABORT_REQ_RCVD);
+ send_abort_rpl(c3cn, rst_status);
+
+ if (!c3cn_flag(c3cn, C3CN_ABORT_RPL_PENDING)) {
+ c3cn->err =
+ abort_status_to_errno(c3cn, req->status, &rst_status);
+ c3cn_closed(c3cn);
+ }
+}
+
+static int do_abort_req(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
+{
+ const struct cpl_abort_req_rss *req = cplhdr(skb);
+ struct s3_conn *c3cn = ctx;
+
+ c3cn_conn_debug("rcv, c3cn 0x%p, s 0x%x, f 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ if (req->status == CPL_ERR_RTX_NEG_ADVICE ||
+ req->status == CPL_ERR_PERSIST_NEG_ADVICE) {
+ __kfree_skb(skb);
+ return 0;
+ }
+
+ process_cpl_msg_ref(process_abort_req, c3cn, skb);
+ return 0;
+}
+
+/*
+ * Process ABORT_RPL_RSS CPL message: -> host
+ * Process abort replies. We only process these messages if we anticipate
+ * them as the coordination between SW and HW in this area is somewhat lacking
+ * and sometimes we get ABORT_RPLs after we are done with the connection that
+ * originated the ABORT_REQ.
+ */
+static void process_abort_rpl(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ if (c3cn_flag(c3cn, C3CN_ABORT_RPL_PENDING)) {
+ if (!c3cn_flag(c3cn, C3CN_ABORT_RPL_RCVD))
+ c3cn_set_flag(c3cn, C3CN_ABORT_RPL_RCVD);
+ else {
+ c3cn_clear_flag(c3cn, C3CN_ABORT_RPL_RCVD);
+ c3cn_clear_flag(c3cn, C3CN_ABORT_RPL_PENDING);
+ if (c3cn_flag(c3cn, C3CN_ABORT_REQ_RCVD))
+ cxgb3i_log_error("%s tid %u, ABORT_RPL_RSS\n",
+ c3cn->cdev->name, c3cn->tid);
+ c3cn_closed(c3cn);
+ }
+ }
+ __kfree_skb(skb);
+}
+
+static int do_abort_rpl(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
+{
+ struct cpl_abort_rpl_rss *rpl = cplhdr(skb);
+ struct s3_conn *c3cn = ctx;
+
+ c3cn_conn_debug("rcv, status 0x%x, c3cn 0x%p, s %u, 0x%lx.\n",
+ rpl->status, c3cn, c3cn ? c3cn->state : 0,
+ c3cn ? c3cn->flags : 0UL);
+
+ /*
+ * Ignore replies to post-close aborts indicating that the abort was
+ * requested too late. These connections are terminated when we get
+ * PEER_CLOSE or CLOSE_CON_RPL and by the time the abort_rpl_rss
+ * arrives the TID is either no longer used or it has been recycled.
+ */
+ if (rpl->status == CPL_ERR_ABORT_FAILED)
+ goto discard;
+
+ /*
+ * Sometimes we've already closed the connection, e.g., a post-close
+ * abort races with ABORT_REQ_RSS, the latter frees the connection
+ * expecting the ABORT_REQ will fail with CPL_ERR_ABORT_FAILED,
+ * but FW turns the ABORT_REQ into a regular one and so we get
+ * ABORT_RPL_RSS with status 0 and no connection.
+ */
+ if (!c3cn)
+ goto discard;
+
+ process_cpl_msg_ref(process_abort_rpl, c3cn, skb);
+ return 0;
+
+discard:
+ __kfree_skb(skb);
+ return 0;
+}
+
+/*
+ * Process RX_ISCSI_HDR CPL message: -> host
+ * Handle received PDUs, the payload could be DDP'ed. If not, the payload
+ * follow after the bhs.
+ */
+static void process_rx_iscsi_hdr(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ struct cpl_iscsi_hdr *hdr_cpl = cplhdr(skb);
+ struct cpl_iscsi_hdr_norss data_cpl;
+ struct cpl_rx_data_ddp_norss ddp_cpl;
+ unsigned int hdr_len, data_len, status;
+ unsigned int len;
+ int err;
+
+ if (unlikely(c3cn->state >= C3CN_STATE_PASSIVE_CLOSE)) {
+ if (c3cn->state != C3CN_STATE_ABORTING)
+ send_abort_req(c3cn);
+ __kfree_skb(skb);
+ return;
+ }
+
+ CXGB3_SKB_CB(skb)->seq = ntohl(hdr_cpl->seq);
+ CXGB3_SKB_CB(skb)->flags = 0;
+
+ skb_reset_transport_header(skb);
+ __skb_pull(skb, sizeof(struct cpl_iscsi_hdr));
+
+ len = hdr_len = ntohs(hdr_cpl->len);
+ /* msg coalesce is off or not enough data received */
+ if (skb->len <= hdr_len) {
+ cxgb3i_log_error("%s: TID %u, ISCSI_HDR, skb len %u < %u.\n",
+ c3cn->cdev->name, c3cn->tid,
+ skb->len, hdr_len);
+ goto abort_conn;
+ }
+
+ err = skb_copy_bits(skb, skb->len - sizeof(ddp_cpl), &ddp_cpl,
+ sizeof(ddp_cpl));
+ if (err < 0)
+ goto abort_conn;
+
+ skb_ulp_mode(skb) = ULP2_FLAG_DATA_READY;
+ skb_ulp_pdulen(skb) = ntohs(ddp_cpl.len);
+ skb_ulp_ddigest(skb) = ntohl(ddp_cpl.ulp_crc);
+ status = ntohl(ddp_cpl.ddp_status);
+
+ c3cn_rx_debug("rx skb 0x%p, len %u, pdulen %u, ddp status 0x%x.\n",
+ skb, skb->len, skb_ulp_pdulen(skb), status);
+
+ if (status & (1 << RX_DDP_STATUS_HCRC_SHIFT))
+ skb_ulp_mode(skb) |= ULP2_FLAG_HCRC_ERROR;
+ if (status & (1 << RX_DDP_STATUS_DCRC_SHIFT))
+ skb_ulp_mode(skb) |= ULP2_FLAG_DCRC_ERROR;
+ if (status & (1 << RX_DDP_STATUS_PAD_SHIFT))
+ skb_ulp_mode(skb) |= ULP2_FLAG_PAD_ERROR;
+
+ if (skb->len > (hdr_len + sizeof(ddp_cpl))) {
+ err = skb_copy_bits(skb, hdr_len, &data_cpl, sizeof(data_cpl));
+ if (err < 0)
+ goto abort_conn;
+ data_len = ntohs(data_cpl.len);
+ len += sizeof(data_cpl) + data_len;
+ } else if (status & (1 << RX_DDP_STATUS_DDP_SHIFT))
+ skb_ulp_mode(skb) |= ULP2_FLAG_DATA_DDPED;
+
+ c3cn->rcv_nxt = ntohl(ddp_cpl.seq) + skb_ulp_pdulen(skb);
+ __pskb_trim(skb, len);
+ __skb_queue_tail(&c3cn->receive_queue, skb);
+ cxgb3i_conn_pdu_ready(c3cn);
+
+ return;
+
+abort_conn:
+ send_abort_req(c3cn);
+ __kfree_skb(skb);
+}
+
+static int do_iscsi_hdr(struct t3cdev *t3dev, struct sk_buff *skb, void *ctx)
+{
+ struct s3_conn *c3cn = ctx;
+
+ process_cpl_msg(process_rx_iscsi_hdr, c3cn, skb);
+ return 0;
+}
+
+/*
+ * Process TX_DATA_ACK CPL messages: -> host
+ * Process an acknowledgment of WR completion. Advance snd_una and send the
+ * next batch of work requests from the write queue.
+ */
+static void process_wr_ack(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ struct cpl_wr_ack *hdr = cplhdr(skb);
+ unsigned int credits = ntohs(hdr->credits);
+ u32 snd_una = ntohl(hdr->snd_una);
+
+ c3cn->wr_avail += credits;
+ if (c3cn->wr_unacked > c3cn->wr_max - c3cn->wr_avail)
+ c3cn->wr_unacked = c3cn->wr_max - c3cn->wr_avail;
+
+ while (credits) {
+ struct sk_buff *p = peek_wr(c3cn);
+
+ if (unlikely(!p)) {
+ cxgb3i_log_error("%u WR_ACK credits for TID %u with "
+ "nothing pending, state %u\n",
+ credits, c3cn->tid, c3cn->state);
+ break;
+ }
+ if (unlikely(credits < p->csum)) {
+ p->csum -= credits;
+ break;
+ } else {
+ dequeue_wr(c3cn);
+ credits -= p->csum;
+ free_wr_skb(p);
+ }
+ }
+
+ if (unlikely(before(snd_una, c3cn->snd_una)))
+ goto out_free;
+
+ if (c3cn->snd_una != snd_una) {
+ c3cn->snd_una = snd_una;
+ dst_confirm(c3cn->dst_cache);
+ }
+
+ if (skb_queue_len(&c3cn->write_queue) && c3cn_push_tx_frames(c3cn, 0))
+ cxgb3i_conn_tx_open(c3cn);
+out_free:
+ __kfree_skb(skb);
+}
+
+static int do_wr_ack(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
+{
+ struct s3_conn *c3cn = ctx;
+
+ process_cpl_msg(process_wr_ack, c3cn, skb);
+ return 0;
+}
+
+/*
+ * for each connection, pre-allocate skbs needed for close/abort requests. So
+ * that we can service the request right away.
+ */
+static void c3cn_free_cpl_skbs(struct s3_conn *c3cn)
+{
+ if (c3cn->cpl_close)
+ kfree_skb(c3cn->cpl_close);
+ if (c3cn->cpl_abort_req)
+ kfree_skb(c3cn->cpl_abort_req);
+ if (c3cn->cpl_abort_rpl)
+ kfree_skb(c3cn->cpl_abort_rpl);
+}
+
+static int c3cn_alloc_cpl_skbs(struct s3_conn *c3cn)
+{
+ c3cn->cpl_close = alloc_skb(sizeof(struct cpl_close_con_req),
+ GFP_KERNEL);
+ if (!c3cn->cpl_close)
+ return -ENOMEM;
+ skb_put(c3cn->cpl_close, sizeof(struct cpl_close_con_req));
+
+ c3cn->cpl_abort_req = alloc_skb(sizeof(struct cpl_abort_req),
+ GFP_KERNEL);
+ if (!c3cn->cpl_abort_req)
+ goto free_cpl_skbs;
+ skb_put(c3cn->cpl_abort_req, sizeof(struct cpl_abort_req));
+
+ c3cn->cpl_abort_rpl = alloc_skb(sizeof(struct cpl_abort_rpl),
+ GFP_KERNEL);
+ if (!c3cn->cpl_abort_rpl)
+ goto free_cpl_skbs;
+ skb_put(c3cn->cpl_abort_rpl, sizeof(struct cpl_abort_rpl));
+
+ return 0;
+
+free_cpl_skbs:
+ c3cn_free_cpl_skbs(c3cn);
+ return -ENOMEM;
+}
+
+/**
+ * c3cn_release_offload_resources - release offload resource
+ * @c3cn: the offloaded iscsi tcp connection.
+ * Release resources held by an offload connection (TID, L2T entry, etc.)
+ */
+static void c3cn_release_offload_resources(struct s3_conn *c3cn)
+{
+ struct t3cdev *cdev = c3cn->cdev;
+ unsigned int tid = c3cn->tid;
+
+ if (!cdev)
+ return;
+
+ c3cn->qset = 0;
+
+ c3cn_free_cpl_skbs(c3cn);
+
+ if (c3cn->wr_avail != c3cn->wr_max) {
+ purge_wr_queue(c3cn);
+ reset_wr_list(c3cn);
+ }
+
+ if (c3cn->l2t) {
+ l2t_release(L2DATA(cdev), c3cn->l2t);
+ c3cn->l2t = NULL;
+ }
+
+ if (c3cn->state == C3CN_STATE_CONNECTING) /* we have ATID */
+ s3_free_atid(cdev, tid);
+ else { /* we have TID */
+ cxgb3_remove_tid(cdev, (void *)c3cn, tid);
+ c3cn_put(c3cn);
+ }
+
+ c3cn->cdev = NULL;
+}
+
+/**
+ * cxgb3i_c3cn_create - allocate and initialize an s3_conn structure
+ * returns the s3_conn structure allocated.
+ */
+struct s3_conn *cxgb3i_c3cn_create(void)
+{
+ struct s3_conn *c3cn;
+
+ c3cn = kzalloc(sizeof(*c3cn), GFP_KERNEL);
+ if (!c3cn)
+ return NULL;
+
+ /* pre-allocate close/abort cpl, so we don't need to wait for memory
+ when close/abort is requested. */
+ if (c3cn_alloc_cpl_skbs(c3cn) < 0)
+ goto free_c3cn;
+
+ c3cn_conn_debug("alloc c3cn 0x%p.\n", c3cn);
+
+ c3cn->flags = 0;
+ spin_lock_init(&c3cn->lock);
+ atomic_set(&c3cn->refcnt, 1);
+ skb_queue_head_init(&c3cn->receive_queue);
+ skb_queue_head_init(&c3cn->write_queue);
+ setup_timer(&c3cn->retry_timer, NULL, (unsigned long)c3cn);
+ rwlock_init(&c3cn->callback_lock);
+
+ return c3cn;
+
+free_c3cn:
+ kfree(c3cn);
+ return NULL;
+}
+
+static void c3cn_active_close(struct s3_conn *c3cn)
+{
+ int data_lost;
+ int close_req = 0;
+
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ dst_confirm(c3cn->dst_cache);
+
+ c3cn_hold(c3cn);
+ spin_lock_bh(&c3cn->lock);
+
+ data_lost = skb_queue_len(&c3cn->receive_queue);
+ __skb_queue_purge(&c3cn->receive_queue);
+
+ switch (c3cn->state) {
+ case C3CN_STATE_CLOSED:
+ case C3CN_STATE_ACTIVE_CLOSE:
+ case C3CN_STATE_CLOSE_WAIT_1:
+ case C3CN_STATE_CLOSE_WAIT_2:
+ case C3CN_STATE_ABORTING:
+ /* nothing need to be done */
+ break;
+ case C3CN_STATE_CONNECTING:
+ /* defer until cpl_act_open_rpl or cpl_act_establish */
+ c3cn_set_flag(c3cn, C3CN_ACTIVE_CLOSE_NEEDED);
+ break;
+ case C3CN_STATE_ESTABLISHED:
+ close_req = 1;
+ c3cn_set_state(c3cn, C3CN_STATE_ACTIVE_CLOSE);
+ break;
+ case C3CN_STATE_PASSIVE_CLOSE:
+ close_req = 1;
+ c3cn_set_state(c3cn, C3CN_STATE_CLOSE_WAIT_2);
+ break;
+ }
+
+ if (close_req) {
+ if (data_lost)
+ /* Unread data was tossed, zap the connection. */
+ send_abort_req(c3cn);
+ else
+ send_close_req(c3cn);
+ }
+
+ spin_unlock_bh(&c3cn->lock);
+ c3cn_put(c3cn);
+}
+
+/**
+ * cxgb3i_c3cn_release - close and release an iscsi tcp connection and any
+ * resource held
+ * @c3cn: the iscsi tcp connection
+ */
+void cxgb3i_c3cn_release(struct s3_conn *c3cn)
+{
+ c3cn_conn_debug("c3cn 0x%p, s %u, f 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+ if (likely(c3cn->state != C3CN_STATE_CONNECTING))
+ c3cn_active_close(c3cn);
+ else
+ c3cn_set_flag(c3cn, C3CN_ACTIVE_CLOSE_NEEDED);
+ c3cn_put(c3cn);
+}
+
+static int is_cxgb3_dev(struct net_device *dev)
+{
+ struct cxgb3i_sdev_data *cdata;
+
+ write_lock(&cdata_rwlock);
+ list_for_each_entry(cdata, &cdata_list, list) {
+ struct adap_ports *ports = &cdata->ports;
+ int i;
+
+ for (i = 0; i < ports->nports; i++)
+ if (dev == ports->lldevs[i]) {
+ write_unlock(&cdata_rwlock);
+ return 1;
+ }
+ }
+ write_unlock(&cdata_rwlock);
+ return 0;
+}
+
+/**
+ * cxgb3_egress_dev - return the cxgb3 egress device
+ * @root_dev: the root device anchoring the search
+ * @c3cn: the connection used to determine egress port in bonding mode
+ * @context: in bonding mode, indicates a connection set up or failover
+ *
+ * Return egress device or NULL if the egress device isn't one of our ports.
+ */
+static struct net_device *cxgb3_egress_dev(struct net_device *root_dev,
+ struct s3_conn *c3cn,
+ int context)
+{
+ while (root_dev) {
+ if (root_dev->priv_flags & IFF_802_1Q_VLAN)
+ root_dev = vlan_dev_real_dev(root_dev);
+ else if (is_cxgb3_dev(root_dev))
+ return root_dev;
+ else
+ return NULL;
+ }
+ return NULL;
+}
+
+static struct rtable *find_route(__be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport)
+{
+ struct rtable *rt;
+ struct flowi fl = {
+ .oif = 0,
+ .nl_u = {
+ .ip4_u = {
+ .daddr = daddr,
+ .saddr = saddr,
+ .tos = 0 } },
+ .proto = IPPROTO_TCP,
+ .uli_u = {
+ .ports = {
+ .sport = sport,
+ .dport = dport } } };
+
+ if (ip_route_output_flow(&init_net, &rt, &fl, NULL, 0))
+ return NULL;
+ return rt;
+}
+
+/*
+ * Assign offload parameters to some connection fields.
+ */
+static void init_offload_conn(struct s3_conn *c3cn,
+ struct t3cdev *cdev,
+ struct dst_entry *dst)
+{
+ BUG_ON(c3cn->cdev != cdev);
+ c3cn->wr_max = c3cn->wr_avail = T3C_DATA(cdev)->max_wrs;
+ c3cn->wr_unacked = 0;
+ c3cn->mss_idx = select_mss(c3cn, dst_mtu(dst));
+
+ reset_wr_list(c3cn);
+}
+
+static int initiate_act_open(struct s3_conn *c3cn, struct net_device *dev)
+{
+ struct cxgb3i_sdev_data *cdata = NDEV2CDATA(dev);
+ struct t3cdev *cdev = cdata->cdev;
+ struct dst_entry *dst = c3cn->dst_cache;
+ struct sk_buff *skb;
+
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+ /*
+ * Initialize connection data. Note that the flags and ULP mode are
+ * initialized higher up ...
+ */
+ c3cn->dev = dev;
+ c3cn->cdev = cdev;
+ c3cn->tid = cxgb3_alloc_atid(cdev, cdata->client, c3cn);
+ if (c3cn->tid < 0)
+ goto out_err;
+
+ c3cn->qset = 0;
+ c3cn->l2t = t3_l2t_get(cdev, dst->neighbour, dev);
+ if (!c3cn->l2t)
+ goto free_tid;
+
+ skb = alloc_skb(sizeof(struct cpl_act_open_req), GFP_KERNEL);
+ if (!skb)
+ goto free_l2t;
+
+ skb->sk = (struct sock *)c3cn;
+ set_arp_failure_handler(skb, act_open_req_arp_failure);
+
+ c3cn_hold(c3cn);
+
+ init_offload_conn(c3cn, cdev, dst);
+ c3cn->err = 0;
+
+ make_act_open_req(c3cn, skb, c3cn->tid, c3cn->l2t);
+ l2t_send(cdev, skb, c3cn->l2t);
+ return 0;
+
+free_l2t:
+ l2t_release(L2DATA(cdev), c3cn->l2t);
+free_tid:
+ s3_free_atid(cdev, c3cn->tid);
+ c3cn->tid = 0;
+out_err:
+ return -1;
+}
+
+
+/**
+ * cxgb3i_c3cn_connect - initiates an iscsi tcp connection to a given address
+ * @c3cn: the iscsi tcp connection
+ * @usin: destination address
+ *
+ * return 0 if active open request is sent, < 0 otherwise.
+ */
+int cxgb3i_c3cn_connect(struct s3_conn *c3cn, struct sockaddr_in *usin)
+{
+ struct rtable *rt;
+ struct net_device *dev;
+ struct cxgb3i_sdev_data *cdata;
+ struct t3cdev *cdev;
+ __be32 sipv4;
+ int err;
+
+ if (usin->sin_family != AF_INET)
+ return -EAFNOSUPPORT;
+
+ c3cn->daddr.sin_port = usin->sin_port;
+ c3cn->daddr.sin_addr.s_addr = usin->sin_addr.s_addr;
+
+ rt = find_route(c3cn->saddr.sin_addr.s_addr,
+ c3cn->daddr.sin_addr.s_addr,
+ c3cn->saddr.sin_port,
+ c3cn->daddr.sin_port);
+ if (rt == NULL) {
+ c3cn_conn_debug("NO route to 0x%x, port %u.\n",
+ c3cn->daddr.sin_addr.s_addr,
+ ntohs(c3cn->daddr.sin_port));
+ return -ENETUNREACH;
+ }
+
+ if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
+ c3cn_conn_debug("multi-cast route to 0x%x, port %u.\n",
+ c3cn->daddr.sin_addr.s_addr,
+ ntohs(c3cn->daddr.sin_port));
+ ip_rt_put(rt);
+ return -ENETUNREACH;
+ }
+
+ if (!c3cn->saddr.sin_addr.s_addr)
+ c3cn->saddr.sin_addr.s_addr = rt->rt_src;
+
+ /* now commit destination to connection */
+ c3cn->dst_cache = &rt->u.dst;
+
+ /* try to establish an offloaded connection */
+ dev = cxgb3_egress_dev(c3cn->dst_cache->dev, c3cn, 0);
+ if (dev == NULL) {
+ c3cn_conn_debug("c3cn 0x%p, egress dev NULL.\n", c3cn);
+ return -ENETUNREACH;
+ }
+ cdata = NDEV2CDATA(dev);
+ cdev = cdata->cdev;
+
+ /* get a source port if one hasn't been provided */
+ err = c3cn_get_port(c3cn, cdata);
+ if (err)
+ return err;
+
+ c3cn_conn_debug("c3cn 0x%p get port %u.\n",
+ c3cn, ntohs(c3cn->saddr.sin_port));
+
+ sipv4 = cxgb3i_get_private_ipv4addr(dev);
+ if (!sipv4) {
+ c3cn_conn_debug("c3cn 0x%p, iscsi ip not configured.\n", c3cn);
+ sipv4 = c3cn->saddr.sin_addr.s_addr;
+ cxgb3i_set_private_ipv4addr(dev, sipv4);
+ } else
+ c3cn->saddr.sin_addr.s_addr = sipv4;
+
+ c3cn_conn_debug("c3cn 0x%p, %u.%u.%u.%u,%u-%u.%u.%u.%u,%u SYN_SENT.\n",
+ c3cn, NIPQUAD(c3cn->saddr.sin_addr.s_addr),
+ ntohs(c3cn->saddr.sin_port),
+ NIPQUAD(c3cn->daddr.sin_addr.s_addr),
+ ntohs(c3cn->daddr.sin_port));
+
+ c3cn_set_state(c3cn, C3CN_STATE_CONNECTING);
+ if (!initiate_act_open(c3cn, dev))
+ return 0;
+
+ /*
+ * If we get here, we don't have an offload connection so simply
+ * return a failure.
+ */
+ err = -ENOTSUPP;
+
+ /*
+ * This trashes the connection and releases the local port,
+ * if necessary.
+ */
+ c3cn_conn_debug("c3cn 0x%p -> CLOSED.\n", c3cn);
+ c3cn_set_state(c3cn, C3CN_STATE_CLOSED);
+ ip_rt_put(rt);
+ c3cn_put_port(c3cn);
+ c3cn->daddr.sin_port = 0;
+ return err;
+}
+
+/**
+ * cxgb3i_c3cn_rx_credits - ack received tcp data.
+ * @c3cn: iscsi tcp connection
+ * @copied: # of bytes processed
+ *
+ * Called after some received data has been read. It returns RX credits
+ * to the HW for the amount of data processed.
+ */
+void cxgb3i_c3cn_rx_credits(struct s3_conn *c3cn, int copied)
+{
+ struct t3cdev *cdev;
+ int must_send;
+ u32 credits, dack = 0;
+
+ if (c3cn->state != C3CN_STATE_ESTABLISHED)
+ return;
+
+ credits = c3cn->copied_seq - c3cn->rcv_wup;
+ if (unlikely(!credits))
+ return;
+
+ cdev = c3cn->cdev;
+
+ if (unlikely(cxgb3_rx_credit_thres == 0))
+ return;
+
+ dack = F_RX_DACK_CHANGE | V_RX_DACK_MODE(1);
+
+ /*
+ * For coalescing to work effectively ensure the receive window has
+ * at least 16KB left.
+ */
+ must_send = credits + 16384 >= cxgb3_rcv_win;
+
+ if (must_send || credits >= cxgb3_rx_credit_thres)
+ c3cn->rcv_wup += send_rx_credits(c3cn, credits, dack);
+}
+
+/**
+ * cxgb3i_c3cn_send_pdus - send the skbs containing iscsi pdus
+ * @c3cn: iscsi tcp connection
+ * @skb: skb contains the iscsi pdu
+ *
+ * Add a list of skbs to a connection send queue. The skbs must comply with
+ * the max size limit of the device and have a headroom of at least
+ * TX_HEADER_LEN bytes.
+ * Return # of bytes queued.
+ */
+int cxgb3i_c3cn_send_pdus(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ struct sk_buff *next;
+ int err, copied = 0;
+
+ spin_lock_bh(&c3cn->lock);
+
+ if (c3cn->state != C3CN_STATE_ESTABLISHED) {
+ c3cn_tx_debug("c3cn 0x%p, not in est. state %u.\n",
+ c3cn, c3cn->state);
+ err = -EAGAIN;
+ goto out_err;
+ }
+
+ err = -EPIPE;
+ if (c3cn->err) {
+ c3cn_tx_debug("c3cn 0x%p, err %d.\n", c3cn, c3cn->err);
+ goto out_err;
+ }
+
+ while (skb) {
+ int frags = skb_shinfo(skb)->nr_frags +
+ (skb->len != skb->data_len);
+
+ if (unlikely(skb_headroom(skb) < TX_HEADER_LEN)) {
+ c3cn_tx_debug("c3cn 0x%p, skb head.\n", c3cn);
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ if (frags >= SKB_WR_LIST_SIZE) {
+ cxgb3i_log_error("c3cn 0x%p, tx frags %d, len %u,%u.\n",
+ c3cn, skb_shinfo(skb)->nr_frags,
+ skb->len, skb->data_len);
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ next = skb->next;
+ skb->next = NULL;
+ skb_entail(c3cn, skb, C3CB_FLAG_NO_APPEND | C3CB_FLAG_NEED_HDR);
+ copied += skb->len;
+ c3cn->write_seq += skb->len + ulp_extra_len(skb);
+ skb = next;
+ }
+done:
+ if (likely(skb_queue_len(&c3cn->write_queue)))
+ c3cn_push_tx_frames(c3cn, 1);
+ spin_unlock_bh(&c3cn->lock);
+ return copied;
+
+out_err:
+ if (copied == 0 && err == -EPIPE)
+ copied = c3cn->err ? c3cn->err : -EPIPE;
+ goto done;
+}
+
+static void sdev_data_cleanup(struct cxgb3i_sdev_data *cdata)
+{
+ struct adap_ports *ports = &cdata->ports;
+ int i;
+
+ for (i = 0; i < ports->nports; i++)
+ NDEV2CDATA(ports->lldevs[i]) = NULL;
+ cxgb3i_free_big_mem(cdata);
+}
+
+void cxgb3i_sdev_cleanup(void)
+{
+ struct cxgb3i_sdev_data *cdata;
+
+ write_lock(&cdata_rwlock);
+ list_for_each_entry(cdata, &cdata_list, list) {
+ list_del(&cdata->list);
+ sdev_data_cleanup(cdata);
+ }
+ write_unlock(&cdata_rwlock);
+}
+
+int cxgb3i_sdev_init(cxgb3_cpl_handler_func *cpl_handlers)
+{
+ cpl_handlers[CPL_ACT_ESTABLISH] = do_act_establish;
+ cpl_handlers[CPL_ACT_OPEN_RPL] = do_act_open_rpl;
+ cpl_handlers[CPL_PEER_CLOSE] = do_peer_close;
+ cpl_handlers[CPL_ABORT_REQ_RSS] = do_abort_req;
+ cpl_handlers[CPL_ABORT_RPL_RSS] = do_abort_rpl;
+ cpl_handlers[CPL_CLOSE_CON_RPL] = do_close_con_rpl;
+ cpl_handlers[CPL_TX_DMA_ACK] = do_wr_ack;
+ cpl_handlers[CPL_ISCSI_HDR] = do_iscsi_hdr;
+
+ if (cxgb3_max_connect > CXGB3I_MAX_CONN)
+ cxgb3_max_connect = CXGB3I_MAX_CONN;
+ return 0;
+}
+
+/**
+ * cxgb3i_sdev_add - allocate and initialize resources for each adapter found
+ * @cdev: t3cdev adapter
+ * @client: cxgb3 driver client
+ */
+void cxgb3i_sdev_add(struct t3cdev *cdev, struct cxgb3_client *client)
+{
+ struct cxgb3i_sdev_data *cdata;
+ struct ofld_page_info rx_page_info;
+ unsigned int wr_len;
+ int mapsize = DIV_ROUND_UP(cxgb3_max_connect,
+ 8 * sizeof(unsigned long));
+ int i;
+
+ cdata = cxgb3i_alloc_big_mem(sizeof(*cdata) + mapsize, GFP_KERNEL);
+ if (!cdata)
+ return;
+
+ if (cdev->ctl(cdev, GET_WR_LEN, &wr_len) < 0 ||
+ cdev->ctl(cdev, GET_PORTS, &cdata->ports) < 0 ||
+ cdev->ctl(cdev, GET_RX_PAGE_INFO, &rx_page_info) < 0)
+ goto free_cdata;
+
+ s3_init_wr_tab(wr_len);
+
+ INIT_LIST_HEAD(&cdata->list);
+ cdata->cdev = cdev;
+ cdata->client = client;
+
+ for (i = 0; i < cdata->ports.nports; i++)
+ NDEV2CDATA(cdata->ports.lldevs[i]) = cdata;
+
+ write_lock(&cdata_rwlock);
+ list_add_tail(&cdata->list, &cdata_list);
+ write_unlock(&cdata_rwlock);
+
+ return;
+
+free_cdata:
+ cxgb3i_free_big_mem(cdata);
+}
+
+/**
+ * cxgb3i_sdev_remove - free the allocated resources for the adapter
+ * @cdev: t3cdev adapter
+ */
+void cxgb3i_sdev_remove(struct t3cdev *cdev)
+{
+ struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(cdev);
+
+ write_lock(&cdata_rwlock);
+ list_del(&cdata->list);
+ write_unlock(&cdata_rwlock);
+
+ sdev_data_cleanup(cdata);
+}
diff --git a/drivers/scsi/cxgb3i/cxgb3i_offload.h b/drivers/scsi/cxgb3i/cxgb3i_offload.h
new file mode 100644
index 000000000000..d23156907ffd
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_offload.h
@@ -0,0 +1,231 @@
+/*
+ * cxgb3i_offload.h: Chelsio S3xx iscsi offloaded tcp connection management
+ *
+ * Copyright (C) 2003-2008 Chelsio Communications. All rights reserved.
+ *
+ * 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 LICENSE file included in this
+ * release for licensing terms and conditions.
+ *
+ * Written by: Dimitris Michailidis (dm@chelsio.com)
+ * Karen Xie (kxie@chelsio.com)
+ */
+
+#ifndef _CXGB3I_OFFLOAD_H
+#define _CXGB3I_OFFLOAD_H
+
+#include <linux/skbuff.h>
+#include <net/tcp.h>
+
+#include "common.h"
+#include "adapter.h"
+#include "t3cdev.h"
+#include "cxgb3_offload.h"
+
+#define cxgb3i_log_error(fmt...) printk(KERN_ERR "cxgb3i: ERR! " fmt)
+#define cxgb3i_log_warn(fmt...) printk(KERN_WARNING "cxgb3i: WARN! " fmt)
+#define cxgb3i_log_info(fmt...) printk(KERN_INFO "cxgb3i: " fmt)
+#define cxgb3i_log_debug(fmt, args...) \
+ printk(KERN_INFO "cxgb3i: %s - " fmt, __func__ , ## args)
+
+/**
+ * struct s3_conn - an iscsi tcp connection structure
+ *
+ * @dev: net device of with connection
+ * @cdev: adapter t3cdev for net device
+ * @flags: see c3cn_flags below
+ * @tid: connection id assigned by the h/w
+ * @qset: queue set used by connection
+ * @mss_idx: Maximum Segment Size table index
+ * @l2t: ARP resolution entry for offload packets
+ * @wr_max: maximum in-flight writes
+ * @wr_avail: number of writes available
+ * @wr_unacked: writes since last request for completion notification
+ * @wr_pending_head: head of pending write queue
+ * @wr_pending_tail: tail of pending write queue
+ * @cpl_close: skb for cpl_close_req
+ * @cpl_abort_req: skb for cpl_abort_req
+ * @cpl_abort_rpl: skb for cpl_abort_rpl
+ * @lock: connection status lock
+ * @refcnt: reference count on connection
+ * @state: connection state
+ * @saddr: source ip/port address
+ * @daddr: destination ip/port address
+ * @dst_cache: reference to destination route
+ * @receive_queue: received PDUs
+ * @write_queue: un-pushed pending writes
+ * @retry_timer: retry timer for various operations
+ * @err: connection error status
+ * @callback_lock: lock for opaque user context
+ * @user_data: opaque user context
+ * @rcv_nxt: next receive seq. #
+ * @copied_seq: head of yet unread data
+ * @rcv_wup: rcv_nxt on last window update sent
+ * @snd_nxt: next sequence we send
+ * @snd_una: first byte we want an ack for
+ * @write_seq: tail+1 of data held in send buffer
+ */
+struct s3_conn {
+ struct net_device *dev;
+ struct t3cdev *cdev;
+ unsigned long flags;
+ int tid;
+ int qset;
+ int mss_idx;
+ struct l2t_entry *l2t;
+ int wr_max;
+ int wr_avail;
+ int wr_unacked;
+ struct sk_buff *wr_pending_head;
+ struct sk_buff *wr_pending_tail;
+ struct sk_buff *cpl_close;
+ struct sk_buff *cpl_abort_req;
+ struct sk_buff *cpl_abort_rpl;
+ spinlock_t lock;
+ atomic_t refcnt;
+ volatile unsigned int state;
+ struct sockaddr_in saddr;
+ struct sockaddr_in daddr;
+ struct dst_entry *dst_cache;
+ struct sk_buff_head receive_queue;
+ struct sk_buff_head write_queue;
+ struct timer_list retry_timer;
+ int err;
+ rwlock_t callback_lock;
+ void *user_data;
+
+ u32 rcv_nxt;
+ u32 copied_seq;
+ u32 rcv_wup;
+ u32 snd_nxt;
+ u32 snd_una;
+ u32 write_seq;
+};
+
+/*
+ * connection state
+ */
+enum conn_states {
+ C3CN_STATE_CONNECTING = 1,
+ C3CN_STATE_ESTABLISHED,
+ C3CN_STATE_ACTIVE_CLOSE,
+ C3CN_STATE_PASSIVE_CLOSE,
+ C3CN_STATE_CLOSE_WAIT_1,
+ C3CN_STATE_CLOSE_WAIT_2,
+ C3CN_STATE_ABORTING,
+ C3CN_STATE_CLOSED,
+};
+
+static inline unsigned int c3cn_is_closing(const struct s3_conn *c3cn)
+{
+ return c3cn->state >= C3CN_STATE_ACTIVE_CLOSE;
+}
+static inline unsigned int c3cn_is_established(const struct s3_conn *c3cn)
+{
+ return c3cn->state == C3CN_STATE_ESTABLISHED;
+}
+
+/*
+ * Connection flags -- many to track some close related events.
+ */
+enum c3cn_flags {
+ C3CN_ABORT_RPL_RCVD, /* received one ABORT_RPL_RSS message */
+ C3CN_ABORT_REQ_RCVD, /* received one ABORT_REQ_RSS message */
+ C3CN_ABORT_RPL_PENDING, /* expecting an abort reply */
+ C3CN_TX_DATA_SENT, /* already sent a TX_DATA WR */
+ C3CN_ACTIVE_CLOSE_NEEDED, /* need to be closed */
+};
+
+/**
+ * cxgb3i_sdev_data - Per adapter data.
+ * Linked off of each Ethernet device port on the adapter.
+ * Also available via the t3cdev structure since we have pointers to our port
+ * net_device's there ...
+ *
+ * @list: list head to link elements
+ * @cdev: t3cdev adapter
+ * @client: CPL client pointer
+ * @ports: array of adapter ports
+ * @sport_map_next: next index into the port map
+ * @sport_map: source port map
+ */
+struct cxgb3i_sdev_data {
+ struct list_head list;
+ struct t3cdev *cdev;
+ struct cxgb3_client *client;
+ struct adap_ports ports;
+ unsigned int sport_map_next;
+ unsigned long sport_map[0];
+};
+#define NDEV2CDATA(ndev) (*(struct cxgb3i_sdev_data **)&(ndev)->ec_ptr)
+#define CXGB3_SDEV_DATA(cdev) NDEV2CDATA((cdev)->lldev)
+
+void cxgb3i_sdev_cleanup(void);
+int cxgb3i_sdev_init(cxgb3_cpl_handler_func *);
+void cxgb3i_sdev_add(struct t3cdev *, struct cxgb3_client *);
+void cxgb3i_sdev_remove(struct t3cdev *);
+
+struct s3_conn *cxgb3i_c3cn_create(void);
+int cxgb3i_c3cn_connect(struct s3_conn *, struct sockaddr_in *);
+void cxgb3i_c3cn_rx_credits(struct s3_conn *, int);
+int cxgb3i_c3cn_send_pdus(struct s3_conn *, struct sk_buff *);
+void cxgb3i_c3cn_release(struct s3_conn *);
+
+/**
+ * cxgb3_skb_cb - control block for received pdu state and ULP mode management.
+ *
+ * @flag: see C3CB_FLAG_* below
+ * @ulp_mode: ULP mode/submode of sk_buff
+ * @seq: tcp sequence number
+ * @ddigest: pdu data digest
+ * @pdulen: recovered pdu length
+ * @wr_data: scratch area for tx wr
+ */
+struct cxgb3_skb_cb {
+ __u8 flags;
+ __u8 ulp_mode;
+ __u32 seq;
+ __u32 ddigest;
+ __u32 pdulen;
+ struct sk_buff *wr_data;
+};
+
+#define CXGB3_SKB_CB(skb) ((struct cxgb3_skb_cb *)&((skb)->cb[0]))
+
+#define skb_ulp_mode(skb) (CXGB3_SKB_CB(skb)->ulp_mode)
+#define skb_ulp_ddigest(skb) (CXGB3_SKB_CB(skb)->ddigest)
+#define skb_ulp_pdulen(skb) (CXGB3_SKB_CB(skb)->pdulen)
+#define skb_wr_data(skb) (CXGB3_SKB_CB(skb)->wr_data)
+
+enum c3cb_flags {
+ C3CB_FLAG_NEED_HDR = 1 << 0, /* packet needs a TX_DATA_WR header */
+ C3CB_FLAG_NO_APPEND = 1 << 1, /* don't grow this skb */
+ C3CB_FLAG_COMPL = 1 << 2, /* request WR completion */
+};
+
+/**
+ * sge_opaque_hdr -
+ * Opaque version of structure the SGE stores at skb->head of TX_DATA packets
+ * and for which we must reserve space.
+ */
+struct sge_opaque_hdr {
+ void *dev;
+ dma_addr_t addr[MAX_SKB_FRAGS + 1];
+};
+
+/* for TX: a skb must have a headroom of at least TX_HEADER_LEN bytes */
+#define TX_HEADER_LEN \
+ (sizeof(struct tx_data_wr) + sizeof(struct sge_opaque_hdr))
+
+/*
+ * get and set private ip for iscsi traffic
+ */
+#define cxgb3i_get_private_ipv4addr(ndev) \
+ (((struct port_info *)(netdev_priv(ndev)))->iscsi_ipv4addr)
+#define cxgb3i_set_private_ipv4addr(ndev, addr) \
+ (((struct port_info *)(netdev_priv(ndev)))->iscsi_ipv4addr) = addr
+
+/* max. connections per adapter */
+#define CXGB3I_MAX_CONN 16384
+#endif /* _CXGB3_OFFLOAD_H */
diff --git a/drivers/scsi/cxgb3i/cxgb3i_pdu.c b/drivers/scsi/cxgb3i/cxgb3i_pdu.c
new file mode 100644
index 000000000000..ce7ce8c6094c
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_pdu.c
@@ -0,0 +1,402 @@
+/*
+ * cxgb3i_pdu.c: Chelsio S3xx iSCSI driver.
+ *
+ * Copyright (c) 2008 Chelsio Communications, Inc.
+ * Copyright (c) 2008 Mike Christie
+ * Copyright (c) 2008 Red Hat, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#include <linux/skbuff.h>
+#include <linux/crypto.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+
+#include "cxgb3i.h"
+#include "cxgb3i_pdu.h"
+
+#ifdef __DEBUG_CXGB3I_RX__
+#define cxgb3i_rx_debug cxgb3i_log_debug
+#else
+#define cxgb3i_rx_debug(fmt...)
+#endif
+
+#ifdef __DEBUG_CXGB3I_TX__
+#define cxgb3i_tx_debug cxgb3i_log_debug
+#else
+#define cxgb3i_tx_debug(fmt...)
+#endif
+
+static struct page *pad_page;
+
+/*
+ * pdu receive, interact with libiscsi_tcp
+ */
+static inline int read_pdu_skb(struct iscsi_conn *conn, struct sk_buff *skb,
+ unsigned int offset, int offloaded)
+{
+ int status = 0;
+ int bytes_read;
+
+ bytes_read = iscsi_tcp_recv_skb(conn, skb, offset, offloaded, &status);
+ switch (status) {
+ case ISCSI_TCP_CONN_ERR:
+ return -EIO;
+ case ISCSI_TCP_SUSPENDED:
+ /* no transfer - just have caller flush queue */
+ return bytes_read;
+ case ISCSI_TCP_SKB_DONE:
+ /*
+ * pdus should always fit in the skb and we should get
+ * segment done notifcation.
+ */
+ iscsi_conn_printk(KERN_ERR, conn, "Invalid pdu or skb.");
+ return -EFAULT;
+ case ISCSI_TCP_SEGMENT_DONE:
+ return bytes_read;
+ default:
+ iscsi_conn_printk(KERN_ERR, conn, "Invalid iscsi_tcp_recv_skb "
+ "status %d\n", status);
+ return -EINVAL;
+ }
+}
+
+static int cxgb3i_conn_read_pdu_skb(struct iscsi_conn *conn,
+ struct sk_buff *skb)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ bool offloaded = 0;
+ unsigned int offset;
+ int rc;
+
+ cxgb3i_rx_debug("conn 0x%p, skb 0x%p, len %u, flag 0x%x.\n",
+ conn, skb, skb->len, skb_ulp_mode(skb));
+
+ if (!iscsi_tcp_recv_segment_is_hdr(tcp_conn)) {
+ iscsi_conn_failure(conn, ISCSI_ERR_PROTO);
+ return -EIO;
+ }
+
+ if (conn->hdrdgst_en && (skb_ulp_mode(skb) & ULP2_FLAG_HCRC_ERROR)) {
+ iscsi_conn_failure(conn, ISCSI_ERR_HDR_DGST);
+ return -EIO;
+ }
+
+ if (conn->datadgst_en && (skb_ulp_mode(skb) & ULP2_FLAG_DCRC_ERROR)) {
+ iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);
+ return -EIO;
+ }
+
+ /* iscsi hdr */
+ rc = read_pdu_skb(conn, skb, 0, 0);
+ if (rc <= 0)
+ return rc;
+
+ if (iscsi_tcp_recv_segment_is_hdr(tcp_conn))
+ return 0;
+
+ offset = rc;
+ if (conn->hdrdgst_en)
+ offset += ISCSI_DIGEST_SIZE;
+
+ /* iscsi data */
+ if (skb_ulp_mode(skb) & ULP2_FLAG_DATA_DDPED) {
+ cxgb3i_rx_debug("skb 0x%p, opcode 0x%x, data %u, ddp'ed, "
+ "itt 0x%x.\n",
+ skb,
+ tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK,
+ tcp_conn->in.datalen,
+ ntohl(tcp_conn->in.hdr->itt));
+ offloaded = 1;
+ } else {
+ cxgb3i_rx_debug("skb 0x%p, opcode 0x%x, data %u, NOT ddp'ed, "
+ "itt 0x%x.\n",
+ skb,
+ tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK,
+ tcp_conn->in.datalen,
+ ntohl(tcp_conn->in.hdr->itt));
+ offset += sizeof(struct cpl_iscsi_hdr_norss);
+ }
+
+ rc = read_pdu_skb(conn, skb, offset, offloaded);
+ if (rc < 0)
+ return rc;
+ else
+ return 0;
+}
+
+/*
+ * pdu transmit, interact with libiscsi_tcp
+ */
+static inline void tx_skb_setmode(struct sk_buff *skb, int hcrc, int dcrc)
+{
+ u8 submode = 0;
+
+ if (hcrc)
+ submode |= 1;
+ if (dcrc)
+ submode |= 2;
+ skb_ulp_mode(skb) = (ULP_MODE_ISCSI << 4) | submode;
+}
+
+void cxgb3i_conn_cleanup_task(struct iscsi_task *task)
+{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+
+ /* never reached the xmit task callout */
+ if (tcp_task->dd_data)
+ kfree_skb(tcp_task->dd_data);
+ tcp_task->dd_data = NULL;
+
+ /* MNC - Do we need a check in case this is called but
+ * cxgb3i_conn_alloc_pdu has never been called on the task */
+ cxgb3i_release_itt(task, task->hdr_itt);
+ iscsi_tcp_cleanup_task(task);
+}
+
+/*
+ * We do not support ahs yet
+ */
+int cxgb3i_conn_alloc_pdu(struct iscsi_task *task, u8 opcode)
+{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct sk_buff *skb;
+
+ task->hdr = NULL;
+ /* always allocate rooms for AHS */
+ skb = alloc_skb(sizeof(struct iscsi_hdr) + ISCSI_MAX_AHS_SIZE +
+ TX_HEADER_LEN, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ cxgb3i_tx_debug("task 0x%p, opcode 0x%x, skb 0x%p.\n",
+ task, opcode, skb);
+
+ tcp_task->dd_data = skb;
+ skb_reserve(skb, TX_HEADER_LEN);
+ task->hdr = (struct iscsi_hdr *)skb->data;
+ task->hdr_max = sizeof(struct iscsi_hdr);
+
+ /* data_out uses scsi_cmd's itt */
+ if (opcode != ISCSI_OP_SCSI_DATA_OUT)
+ cxgb3i_reserve_itt(task, &task->hdr->itt);
+
+ return 0;
+}
+
+int cxgb3i_conn_init_pdu(struct iscsi_task *task, unsigned int offset,
+ unsigned int count)
+{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct sk_buff *skb = tcp_task->dd_data;
+ struct iscsi_conn *conn = task->conn;
+ struct page *pg;
+ unsigned int datalen = count;
+ int i, padlen = iscsi_padding(count);
+ skb_frag_t *frag;
+
+ cxgb3i_tx_debug("task 0x%p,0x%p, offset %u, count %u, skb 0x%p.\n",
+ task, task->sc, offset, count, skb);
+
+ skb_put(skb, task->hdr_len);
+ tx_skb_setmode(skb, conn->hdrdgst_en, datalen ? conn->datadgst_en : 0);
+ if (!count)
+ return 0;
+
+ if (task->sc) {
+ struct scatterlist *sg;
+ struct scsi_data_buffer *sdb;
+ unsigned int sgoffset = offset;
+ struct page *sgpg;
+ unsigned int sglen;
+
+ sdb = scsi_out(task->sc);
+ sg = sdb->table.sgl;
+
+ for_each_sg(sdb->table.sgl, sg, sdb->table.nents, i) {
+ cxgb3i_tx_debug("sg %d, page 0x%p, len %u offset %u\n",
+ i, sg_page(sg), sg->length, sg->offset);
+
+ if (sgoffset < sg->length)
+ break;
+ sgoffset -= sg->length;
+ }
+ sgpg = sg_page(sg);
+ sglen = sg->length - sgoffset;
+
+ do {
+ int j = skb_shinfo(skb)->nr_frags;
+ unsigned int copy;
+
+ if (!sglen) {
+ sg = sg_next(sg);
+ sgpg = sg_page(sg);
+ sgoffset = 0;
+ sglen = sg->length;
+ ++i;
+ }
+ copy = min(sglen, datalen);
+ if (j && skb_can_coalesce(skb, j, sgpg,
+ sg->offset + sgoffset)) {
+ skb_shinfo(skb)->frags[j - 1].size += copy;
+ } else {
+ get_page(sgpg);
+ skb_fill_page_desc(skb, j, sgpg,
+ sg->offset + sgoffset, copy);
+ }
+ sgoffset += copy;
+ sglen -= copy;
+ datalen -= copy;
+ } while (datalen);
+ } else {
+ pg = virt_to_page(task->data);
+
+ while (datalen) {
+ i = skb_shinfo(skb)->nr_frags;
+ frag = &skb_shinfo(skb)->frags[i];
+
+ get_page(pg);
+ frag->page = pg;
+ frag->page_offset = 0;
+ frag->size = min((unsigned int)PAGE_SIZE, datalen);
+
+ skb_shinfo(skb)->nr_frags++;
+ datalen -= frag->size;
+ pg++;
+ }
+ }
+
+ if (padlen) {
+ i = skb_shinfo(skb)->nr_frags;
+ frag = &skb_shinfo(skb)->frags[i];
+ frag->page = pad_page;
+ frag->page_offset = 0;
+ frag->size = padlen;
+ skb_shinfo(skb)->nr_frags++;
+ }
+
+ datalen = count + padlen;
+ skb->data_len += datalen;
+ skb->truesize += datalen;
+ skb->len += datalen;
+ return 0;
+}
+
+int cxgb3i_conn_xmit_pdu(struct iscsi_task *task)
+{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct sk_buff *skb = tcp_task->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = task->conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ unsigned int datalen;
+ int err;
+
+ if (!skb)
+ return 0;
+
+ datalen = skb->data_len;
+ tcp_task->dd_data = NULL;
+ err = cxgb3i_c3cn_send_pdus(cconn->cep->c3cn, skb);
+ cxgb3i_tx_debug("task 0x%p, skb 0x%p, len %u/%u, rv %d.\n",
+ task, skb, skb->len, skb->data_len, err);
+ if (err > 0) {
+ int pdulen = err;
+
+ if (task->conn->hdrdgst_en)
+ pdulen += ISCSI_DIGEST_SIZE;
+ if (datalen && task->conn->datadgst_en)
+ pdulen += ISCSI_DIGEST_SIZE;
+
+ task->conn->txdata_octets += pdulen;
+ return 0;
+ }
+
+ if (err < 0 && err != -EAGAIN) {
+ kfree_skb(skb);
+ cxgb3i_tx_debug("itt 0x%x, skb 0x%p, len %u/%u, xmit err %d.\n",
+ task->itt, skb, skb->len, skb->data_len, err);
+ iscsi_conn_printk(KERN_ERR, task->conn, "xmit err %d.\n", err);
+ iscsi_conn_failure(task->conn, ISCSI_ERR_XMIT_FAILED);
+ return err;
+ }
+ /* reset skb to send when we are called again */
+ tcp_task->dd_data = skb;
+ return -EAGAIN;
+}
+
+int cxgb3i_pdu_init(void)
+{
+ pad_page = alloc_page(GFP_KERNEL);
+ if (!pad_page)
+ return -ENOMEM;
+ memset(page_address(pad_page), 0, PAGE_SIZE);
+ return 0;
+}
+
+void cxgb3i_pdu_cleanup(void)
+{
+ if (pad_page) {
+ __free_page(pad_page);
+ pad_page = NULL;
+ }
+}
+
+void cxgb3i_conn_pdu_ready(struct s3_conn *c3cn)
+{
+ struct sk_buff *skb;
+ unsigned int read = 0;
+ struct iscsi_conn *conn = c3cn->user_data;
+ int err = 0;
+
+ cxgb3i_rx_debug("cn 0x%p.\n", c3cn);
+
+ read_lock(&c3cn->callback_lock);
+ if (unlikely(!conn || conn->suspend_rx)) {
+ cxgb3i_rx_debug("conn 0x%p, id %d, suspend_rx %lu!\n",
+ conn, conn ? conn->id : 0xFF,
+ conn ? conn->suspend_rx : 0xFF);
+ read_unlock(&c3cn->callback_lock);
+ return;
+ }
+ skb = skb_peek(&c3cn->receive_queue);
+ while (!err && skb) {
+ __skb_unlink(skb, &c3cn->receive_queue);
+ read += skb_ulp_pdulen(skb);
+ err = cxgb3i_conn_read_pdu_skb(conn, skb);
+ __kfree_skb(skb);
+ skb = skb_peek(&c3cn->receive_queue);
+ }
+ read_unlock(&c3cn->callback_lock);
+ if (c3cn) {
+ c3cn->copied_seq += read;
+ cxgb3i_c3cn_rx_credits(c3cn, read);
+ }
+ conn->rxdata_octets += read;
+}
+
+void cxgb3i_conn_tx_open(struct s3_conn *c3cn)
+{
+ struct iscsi_conn *conn = c3cn->user_data;
+
+ cxgb3i_tx_debug("cn 0x%p.\n", c3cn);
+ if (conn) {
+ cxgb3i_tx_debug("cn 0x%p, cid %d.\n", c3cn, conn->id);
+ scsi_queue_work(conn->session->host, &conn->xmitwork);
+ }
+}
+
+void cxgb3i_conn_closing(struct s3_conn *c3cn)
+{
+ struct iscsi_conn *conn;
+
+ read_lock(&c3cn->callback_lock);
+ conn = c3cn->user_data;
+ if (conn && c3cn->state != C3CN_STATE_ESTABLISHED)
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ read_unlock(&c3cn->callback_lock);
+}
diff --git a/drivers/scsi/cxgb3i/cxgb3i_pdu.h b/drivers/scsi/cxgb3i/cxgb3i_pdu.h
new file mode 100644
index 000000000000..a3f685cc2362
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_pdu.h
@@ -0,0 +1,59 @@
+/*
+ * cxgb3i_ulp2.h: Chelsio S3xx iSCSI driver.
+ *
+ * Copyright (c) 2008 Chelsio Communications, 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.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#ifndef __CXGB3I_ULP2_PDU_H__
+#define __CXGB3I_ULP2_PDU_H__
+
+struct cpl_iscsi_hdr_norss {
+ union opcode_tid ot;
+ u16 pdu_len_ddp;
+ u16 len;
+ u32 seq;
+ u16 urg;
+ u8 rsvd;
+ u8 status;
+};
+
+struct cpl_rx_data_ddp_norss {
+ union opcode_tid ot;
+ u16 urg;
+ u16 len;
+ u32 seq;
+ u32 nxt_seq;
+ u32 ulp_crc;
+ u32 ddp_status;
+};
+
+#define RX_DDP_STATUS_IPP_SHIFT 27 /* invalid pagepod */
+#define RX_DDP_STATUS_TID_SHIFT 26 /* tid mismatch */
+#define RX_DDP_STATUS_COLOR_SHIFT 25 /* color mismatch */
+#define RX_DDP_STATUS_OFFSET_SHIFT 24 /* offset mismatch */
+#define RX_DDP_STATUS_ULIMIT_SHIFT 23 /* ulimit error */
+#define RX_DDP_STATUS_TAG_SHIFT 22 /* tag mismatch */
+#define RX_DDP_STATUS_DCRC_SHIFT 21 /* dcrc error */
+#define RX_DDP_STATUS_HCRC_SHIFT 20 /* hcrc error */
+#define RX_DDP_STATUS_PAD_SHIFT 19 /* pad error */
+#define RX_DDP_STATUS_PPP_SHIFT 18 /* pagepod parity error */
+#define RX_DDP_STATUS_LLIMIT_SHIFT 17 /* llimit error */
+#define RX_DDP_STATUS_DDP_SHIFT 16 /* ddp'able */
+#define RX_DDP_STATUS_PMM_SHIFT 15 /* pagepod mismatch */
+
+#define ULP2_FLAG_DATA_READY 0x1
+#define ULP2_FLAG_DATA_DDPED 0x2
+#define ULP2_FLAG_HCRC_ERROR 0x10
+#define ULP2_FLAG_DCRC_ERROR 0x20
+#define ULP2_FLAG_PAD_ERROR 0x40
+
+void cxgb3i_conn_closing(struct s3_conn *);
+void cxgb3i_conn_pdu_ready(struct s3_conn *c3cn);
+void cxgb3i_conn_tx_open(struct s3_conn *c3cn);
+#endif
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
index 3d50cabca7ee..53664765570a 100644
--- a/drivers/scsi/device_handler/scsi_dh_rdac.c
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -24,6 +24,7 @@
#include <scsi/scsi_dh.h>
#define RDAC_NAME "rdac"
+#define RDAC_RETRY_COUNT 5
/*
* LSI mode page stuff
@@ -386,6 +387,7 @@ static int check_ownership(struct scsi_device *sdev, struct rdac_dh_data *h)
struct c9_inquiry *inqp;
h->lun_state = RDAC_LUN_UNOWNED;
+ h->state = RDAC_STATE_ACTIVE;
err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry), h);
if (err == SCSI_DH_OK) {
inqp = &h->inq.c9;
@@ -477,21 +479,27 @@ static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
{
struct request *rq;
struct request_queue *q = sdev->request_queue;
- int err = SCSI_DH_RES_TEMP_UNAVAIL;
+ int err, retry_cnt = RDAC_RETRY_COUNT;
+retry:
+ err = SCSI_DH_RES_TEMP_UNAVAIL;
rq = rdac_failover_get(sdev, h);
if (!rq)
goto done;
- sdev_printk(KERN_INFO, sdev, "queueing MODE_SELECT command.\n");
+ sdev_printk(KERN_INFO, sdev, "%s MODE_SELECT command.\n",
+ (retry_cnt == RDAC_RETRY_COUNT) ? "queueing" : "retrying");
err = blk_execute_rq(q, NULL, rq, 1);
- if (err != SCSI_DH_OK)
+ blk_put_request(rq);
+ if (err != SCSI_DH_OK) {
err = mode_select_handle_sense(sdev, h->sense);
+ if (err == SCSI_DH_RETRY && retry_cnt--)
+ goto retry;
+ }
if (err == SCSI_DH_OK)
h->state = RDAC_STATE_ACTIVE;
- blk_put_request(rq);
done:
return err;
}
@@ -594,6 +602,8 @@ static const struct scsi_dh_devlist rdac_dev_list[] = {
{"SUN", "LCSM100_F"},
{"DELL", "MD3000"},
{"DELL", "MD3000i"},
+ {"LSI", "INF-01-00"},
+ {"ENGENIO", "INF-01-00"},
{NULL, NULL},
};
diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c
index a73a6bbb1b2b..976cdd5c94ef 100644
--- a/drivers/scsi/eata.c
+++ b/drivers/scsi/eata.c
@@ -1626,8 +1626,15 @@ static void map_dma(unsigned int i, struct hostdata *ha)
cpp->sense_len = SCSI_SENSE_BUFFERSIZE;
- count = scsi_dma_map(SCpnt);
- BUG_ON(count < 0);
+ if (!scsi_sg_count(SCpnt)) {
+ cpp->data_len = 0;
+ return;
+ }
+
+ count = pci_map_sg(ha->pdev, scsi_sglist(SCpnt), scsi_sg_count(SCpnt),
+ pci_dir);
+ BUG_ON(!count);
+
scsi_for_each_sg(SCpnt, sg, count, k) {
cpp->sglist[k].address = H2DEV(sg_dma_address(sg));
cpp->sglist[k].num_bytes = H2DEV(sg_dma_len(sg));
@@ -1655,7 +1662,9 @@ static void unmap_dma(unsigned int i, struct hostdata *ha)
pci_unmap_single(ha->pdev, DEV2H(cpp->sense_addr),
DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE);
- scsi_dma_unmap(SCpnt);
+ if (scsi_sg_count(SCpnt))
+ pci_unmap_sg(ha->pdev, scsi_sglist(SCpnt), scsi_sg_count(SCpnt),
+ pci_dir);
if (!DEV2H(cpp->data_len))
pci_dir = PCI_DMA_BIDIRECTIONAL;
diff --git a/drivers/scsi/eata_pio.c b/drivers/scsi/eata_pio.c
index 952505c006df..152dd15db276 100644
--- a/drivers/scsi/eata_pio.c
+++ b/drivers/scsi/eata_pio.c
@@ -14,8 +14,8 @@
* neuffer@goofy.zdv.uni-mainz.de *
* a.arnold@kfa-juelich.de *
* *
- * Updated 2002 by Alan Cox <alan@redhat.com> for Linux *
- * 2.5.x and the newer locking and error handling *
+ * Updated 2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> for *
+ * Linux 2.5.x and the newer locking and error handling *
* *
* This program is free software; you can redistribute it *
* and/or modify it under the terms of the GNU General *
diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c
index 62a4618530d0..a680e18b5f3b 100644
--- a/drivers/scsi/esp_scsi.c
+++ b/drivers/scsi/esp_scsi.c
@@ -1453,7 +1453,7 @@ static void esp_msgin_sdtr(struct esp *esp, struct esp_target_data *tp)
offset = 0;
if (offset) {
- int rounded_up, one_clock;
+ int one_clock;
if (period > esp->max_period) {
period = offset = 0;
@@ -1463,9 +1463,7 @@ static void esp_msgin_sdtr(struct esp *esp, struct esp_target_data *tp)
goto do_reject;
one_clock = esp->ccycle / 1000;
- rounded_up = (period << 2);
- rounded_up = (rounded_up + one_clock - 1) / one_clock;
- stp = rounded_up;
+ stp = DIV_ROUND_UP(period << 2, one_clock);
if (stp && esp->rev >= FAS236) {
if (stp >= 50)
stp--;
diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
new file mode 100644
index 000000000000..b78da06d7c0e
--- /dev/null
+++ b/drivers/scsi/fcoe/Makefile
@@ -0,0 +1,8 @@
+# $Id: Makefile
+
+obj-$(CONFIG_FCOE) += fcoe.o
+
+fcoe-y := \
+ libfcoe.o \
+ fcoe_sw.o \
+ fc_transport_fcoe.o
diff --git a/drivers/scsi/fcoe/fc_transport_fcoe.c b/drivers/scsi/fcoe/fc_transport_fcoe.c
new file mode 100644
index 000000000000..bf7fe6fc0820
--- /dev/null
+++ b/drivers/scsi/fcoe/fc_transport_fcoe.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/pci.h>
+#include <scsi/libfcoe.h>
+#include <scsi/fc_transport_fcoe.h>
+
+/* internal fcoe transport */
+struct fcoe_transport_internal {
+ struct fcoe_transport *t;
+ struct net_device *netdev;
+ struct list_head list;
+};
+
+/* fcoe transports list and its lock */
+static LIST_HEAD(fcoe_transports);
+static DEFINE_MUTEX(fcoe_transports_lock);
+
+/**
+ * fcoe_transport_default - returns ptr to the default transport fcoe_sw
+ **/
+struct fcoe_transport *fcoe_transport_default(void)
+{
+ return &fcoe_sw_transport;
+}
+
+/**
+ * fcoe_transport_to_pcidev - get the pci dev from a netdev
+ * @netdev: the netdev that pci dev will be retrived from
+ *
+ * Returns: NULL or the corrsponding pci_dev
+ **/
+struct pci_dev *fcoe_transport_pcidev(const struct net_device *netdev)
+{
+ if (!netdev->dev.parent)
+ return NULL;
+ return to_pci_dev(netdev->dev.parent);
+}
+
+/**
+ * fcoe_transport_device_lookup - find out netdev is managed by the
+ * transport
+ * assign a transport to a device
+ * @netdev: the netdev the transport to be attached to
+ *
+ * This will look for existing offload driver, if not found, it falls back to
+ * the default sw hba (fcoe_sw) as its fcoe transport.
+ *
+ * Returns: 0 for success
+ **/
+static struct fcoe_transport_internal *fcoe_transport_device_lookup(
+ struct fcoe_transport *t, struct net_device *netdev)
+{
+ struct fcoe_transport_internal *ti;
+
+ /* assign the transpor to this device */
+ mutex_lock(&t->devlock);
+ list_for_each_entry(ti, &t->devlist, list) {
+ if (ti->netdev == netdev) {
+ mutex_unlock(&t->devlock);
+ return ti;
+ }
+ }
+ mutex_unlock(&t->devlock);
+ return NULL;
+}
+/**
+ * fcoe_transport_device_add - assign a transport to a device
+ * @netdev: the netdev the transport to be attached to
+ *
+ * This will look for existing offload driver, if not found, it falls back to
+ * the default sw hba (fcoe_sw) as its fcoe transport.
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_transport_device_add(struct fcoe_transport *t,
+ struct net_device *netdev)
+{
+ struct fcoe_transport_internal *ti;
+
+ ti = fcoe_transport_device_lookup(t, netdev);
+ if (ti) {
+ printk(KERN_DEBUG "fcoe_transport_device_add:"
+ "device %s is already added to transport %s\n",
+ netdev->name, t->name);
+ return -EEXIST;
+ }
+ /* allocate an internal struct to host the netdev and the list */
+ ti = kzalloc(sizeof(*ti), GFP_KERNEL);
+ if (!ti)
+ return -ENOMEM;
+
+ ti->t = t;
+ ti->netdev = netdev;
+ INIT_LIST_HEAD(&ti->list);
+ dev_hold(ti->netdev);
+
+ mutex_lock(&t->devlock);
+ list_add(&ti->list, &t->devlist);
+ mutex_unlock(&t->devlock);
+
+ printk(KERN_DEBUG "fcoe_transport_device_add:"
+ "device %s added to transport %s\n",
+ netdev->name, t->name);
+
+ return 0;
+}
+
+/**
+ * fcoe_transport_device_remove - remove a device from its transport
+ * @netdev: the netdev the transport to be attached to
+ *
+ * this removes the device from the transport so the given transport will
+ * not manage this device any more
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_transport_device_remove(struct fcoe_transport *t,
+ struct net_device *netdev)
+{
+ struct fcoe_transport_internal *ti;
+
+ ti = fcoe_transport_device_lookup(t, netdev);
+ if (!ti) {
+ printk(KERN_DEBUG "fcoe_transport_device_remove:"
+ "device %s is not managed by transport %s\n",
+ netdev->name, t->name);
+ return -ENODEV;
+ }
+ mutex_lock(&t->devlock);
+ list_del(&ti->list);
+ mutex_unlock(&t->devlock);
+ printk(KERN_DEBUG "fcoe_transport_device_remove:"
+ "device %s removed from transport %s\n",
+ netdev->name, t->name);
+ dev_put(ti->netdev);
+ kfree(ti);
+ return 0;
+}
+
+/**
+ * fcoe_transport_device_remove_all - remove all from transport devlist
+ *
+ * this removes the device from the transport so the given transport will
+ * not manage this device any more
+ *
+ * Returns: 0 for success
+ **/
+static void fcoe_transport_device_remove_all(struct fcoe_transport *t)
+{
+ struct fcoe_transport_internal *ti, *tmp;
+
+ mutex_lock(&t->devlock);
+ list_for_each_entry_safe(ti, tmp, &t->devlist, list) {
+ list_del(&ti->list);
+ kfree(ti);
+ }
+ mutex_unlock(&t->devlock);
+}
+
+/**
+ * fcoe_transport_match - use the bus device match function to match the hw
+ * @t: the fcoe transport
+ * @netdev:
+ *
+ * This function is used to check if the givne transport wants to manage the
+ * input netdev. if the transports implements the match function, it will be
+ * called, o.w. we just compare the pci vendor and device id.
+ *
+ * Returns: true for match up
+ **/
+static bool fcoe_transport_match(struct fcoe_transport *t,
+ struct net_device *netdev)
+{
+ /* match transport by vendor and device id */
+ struct pci_dev *pci;
+
+ pci = fcoe_transport_pcidev(netdev);
+
+ if (pci) {
+ printk(KERN_DEBUG "fcoe_transport_match:"
+ "%s:%x:%x -- %s:%x:%x\n",
+ t->name, t->vendor, t->device,
+ netdev->name, pci->vendor, pci->device);
+
+ /* if transport supports match */
+ if (t->match)
+ return t->match(netdev);
+
+ /* else just compare the vendor and device id: pci only */
+ return (t->vendor == pci->vendor) && (t->device == pci->device);
+ }
+ return false;
+}
+
+/**
+ * fcoe_transport_lookup - check if the transport is already registered
+ * @t: the transport to be looked up
+ *
+ * This compares the parent device (pci) vendor and device id
+ *
+ * Returns: NULL if not found
+ *
+ * TODO - return default sw transport if no other transport is found
+ **/
+static struct fcoe_transport *fcoe_transport_lookup(
+ struct net_device *netdev)
+{
+ struct fcoe_transport *t;
+
+ mutex_lock(&fcoe_transports_lock);
+ list_for_each_entry(t, &fcoe_transports, list) {
+ if (fcoe_transport_match(t, netdev)) {
+ mutex_unlock(&fcoe_transports_lock);
+ return t;
+ }
+ }
+ mutex_unlock(&fcoe_transports_lock);
+
+ printk(KERN_DEBUG "fcoe_transport_lookup:"
+ "use default transport for %s\n", netdev->name);
+ return fcoe_transport_default();
+}
+
+/**
+ * fcoe_transport_register - adds a fcoe transport to the fcoe transports list
+ * @t: ptr to the fcoe transport to be added
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_transport_register(struct fcoe_transport *t)
+{
+ struct fcoe_transport *tt;
+
+ /* TODO - add fcoe_transport specific initialization here */
+ mutex_lock(&fcoe_transports_lock);
+ list_for_each_entry(tt, &fcoe_transports, list) {
+ if (tt == t) {
+ mutex_unlock(&fcoe_transports_lock);
+ return -EEXIST;
+ }
+ }
+ list_add_tail(&t->list, &fcoe_transports);
+ mutex_unlock(&fcoe_transports_lock);
+
+ mutex_init(&t->devlock);
+ INIT_LIST_HEAD(&t->devlist);
+
+ printk(KERN_DEBUG "fcoe_transport_register:%s\n", t->name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_transport_register);
+
+/**
+ * fcoe_transport_unregister - remove the tranport fro the fcoe transports list
+ * @t: ptr to the fcoe transport to be removed
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_transport_unregister(struct fcoe_transport *t)
+{
+ struct fcoe_transport *tt, *tmp;
+
+ mutex_lock(&fcoe_transports_lock);
+ list_for_each_entry_safe(tt, tmp, &fcoe_transports, list) {
+ if (tt == t) {
+ list_del(&t->list);
+ mutex_unlock(&fcoe_transports_lock);
+ fcoe_transport_device_remove_all(t);
+ printk(KERN_DEBUG "fcoe_transport_unregister:%s\n",
+ t->name);
+ return 0;
+ }
+ }
+ mutex_unlock(&fcoe_transports_lock);
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(fcoe_transport_unregister);
+
+/*
+ * fcoe_load_transport_driver - load an offload driver by alias name
+ * @netdev: the target net device
+ *
+ * Requests for an offload driver module as the fcoe transport, if fails, it
+ * falls back to use the SW HBA (fcoe_sw) as its transport
+ *
+ * TODO -
+ * 1. supports only PCI device
+ * 2. needs fix for VLAn and bonding
+ * 3. pure hw fcoe hba may not have netdev
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_load_transport_driver(struct net_device *netdev)
+{
+ struct pci_dev *pci;
+ struct device *dev = netdev->dev.parent;
+
+ if (fcoe_transport_lookup(netdev)) {
+ /* load default transport */
+ printk(KERN_DEBUG "fcoe: already loaded transport for %s\n",
+ netdev->name);
+ return -EEXIST;
+ }
+
+ pci = to_pci_dev(dev);
+ if (dev->bus != &pci_bus_type) {
+ printk(KERN_DEBUG "fcoe: support noly PCI device\n");
+ return -ENODEV;
+ }
+ printk(KERN_DEBUG "fcoe: loading driver fcoe-pci-0x%04x-0x%04x\n",
+ pci->vendor, pci->device);
+
+ return request_module("fcoe-pci-0x%04x-0x%04x",
+ pci->vendor, pci->device);
+
+}
+EXPORT_SYMBOL_GPL(fcoe_load_transport_driver);
+
+/**
+ * fcoe_transport_attach - load transport to fcoe
+ * @netdev: the netdev the transport to be attached to
+ *
+ * This will look for existing offload driver, if not found, it falls back to
+ * the default sw hba (fcoe_sw) as its fcoe transport.
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_transport_attach(struct net_device *netdev)
+{
+ struct fcoe_transport *t;
+
+ /* find the corresponding transport */
+ t = fcoe_transport_lookup(netdev);
+ if (!t) {
+ printk(KERN_DEBUG "fcoe_transport_attach"
+ ":no transport for %s:use %s\n",
+ netdev->name, t->name);
+ return -ENODEV;
+ }
+ /* add to the transport */
+ if (fcoe_transport_device_add(t, netdev)) {
+ printk(KERN_DEBUG "fcoe_transport_attach"
+ ":failed to add %s to tramsport %s\n",
+ netdev->name, t->name);
+ return -EIO;
+ }
+ /* transport create function */
+ if (t->create)
+ t->create(netdev);
+
+ printk(KERN_DEBUG "fcoe_transport_attach:transport %s for %s\n",
+ t->name, netdev->name);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_transport_attach);
+
+/**
+ * fcoe_transport_release - unload transport from fcoe
+ * @netdev: the net device on which fcoe is to be released
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_transport_release(struct net_device *netdev)
+{
+ struct fcoe_transport *t;
+
+ /* find the corresponding transport */
+ t = fcoe_transport_lookup(netdev);
+ if (!t) {
+ printk(KERN_DEBUG "fcoe_transport_release:"
+ "no transport for %s:use %s\n",
+ netdev->name, t->name);
+ return -ENODEV;
+ }
+ /* remove the device from the transport */
+ if (fcoe_transport_device_remove(t, netdev)) {
+ printk(KERN_DEBUG "fcoe_transport_release:"
+ "failed to add %s to tramsport %s\n",
+ netdev->name, t->name);
+ return -EIO;
+ }
+ /* transport destroy function */
+ if (t->destroy)
+ t->destroy(netdev);
+
+ printk(KERN_DEBUG "fcoe_transport_release:"
+ "device %s dettached from transport %s\n",
+ netdev->name, t->name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_transport_release);
+
+/**
+ * fcoe_transport_init - initializes fcoe transport layer
+ *
+ * This prepares for the fcoe transport layer
+ *
+ * Returns: none
+ **/
+int __init fcoe_transport_init(void)
+{
+ INIT_LIST_HEAD(&fcoe_transports);
+ mutex_init(&fcoe_transports_lock);
+ return 0;
+}
+
+/**
+ * fcoe_transport_exit - cleans up the fcoe transport layer
+ * This cleans up the fcoe transport layer. removing any transport on the list,
+ * note that the transport destroy func is not called here.
+ *
+ * Returns: none
+ **/
+int __exit fcoe_transport_exit(void)
+{
+ struct fcoe_transport *t, *tmp;
+
+ mutex_lock(&fcoe_transports_lock);
+ list_for_each_entry_safe(t, tmp, &fcoe_transports, list) {
+ list_del(&t->list);
+ mutex_unlock(&fcoe_transports_lock);
+ fcoe_transport_device_remove_all(t);
+ mutex_lock(&fcoe_transports_lock);
+ }
+ mutex_unlock(&fcoe_transports_lock);
+ return 0;
+}
diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c
new file mode 100644
index 000000000000..dc4cd5e25760
--- /dev/null
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <net/rtnetlink.h>
+
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_encaps.h>
+#include <scsi/fc/fc_fs.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include <scsi/libfc.h>
+#include <scsi/libfcoe.h>
+#include <scsi/fc_transport_fcoe.h>
+
+#define FCOE_SW_VERSION "0.1"
+#define FCOE_SW_NAME "fcoesw"
+#define FCOE_SW_VENDOR "Open-FCoE.org"
+
+#define FCOE_MAX_LUN 255
+#define FCOE_MAX_FCP_TARGET 256
+
+#define FCOE_MAX_OUTSTANDING_COMMANDS 1024
+
+#define FCOE_MIN_XID 0x0001 /* the min xid supported by fcoe_sw */
+#define FCOE_MAX_XID 0x07ef /* the max xid supported by fcoe_sw */
+
+static struct scsi_transport_template *scsi_transport_fcoe_sw;
+
+struct fc_function_template fcoe_sw_transport_function = {
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_active_fc4s = 1,
+ .show_host_maxframe_size = 1,
+
+ .show_host_port_id = 1,
+ .show_host_supported_speeds = 1,
+ .get_host_speed = fc_get_host_speed,
+ .show_host_speed = 1,
+ .show_host_port_type = 1,
+ .get_host_port_state = fc_get_host_port_state,
+ .show_host_port_state = 1,
+ .show_host_symbolic_name = 1,
+
+ .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+
+ .show_host_fabric_name = 1,
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+ .show_starget_port_id = 1,
+ .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+ .get_fc_host_stats = fc_get_host_stats,
+ .issue_fc_host_lip = fcoe_reset,
+
+ .terminate_rport_io = fc_rport_terminate_io,
+};
+
+static struct scsi_host_template fcoe_sw_shost_template = {
+ .module = THIS_MODULE,
+ .name = "FCoE Driver",
+ .proc_name = FCOE_SW_NAME,
+ .queuecommand = fc_queuecommand,
+ .eh_abort_handler = fc_eh_abort,
+ .eh_device_reset_handler = fc_eh_device_reset,
+ .eh_host_reset_handler = fc_eh_host_reset,
+ .slave_alloc = fc_slave_alloc,
+ .change_queue_depth = fc_change_queue_depth,
+ .change_queue_type = fc_change_queue_type,
+ .this_id = -1,
+ .cmd_per_lun = 32,
+ .can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sg_tablesize = SG_ALL,
+ .max_sectors = 0xffff,
+};
+
+/*
+ * fcoe_sw_lport_config - sets up the fc_lport
+ * @lp: ptr to the fc_lport
+ * @shost: ptr to the parent scsi host
+ *
+ * Returns: 0 for success
+ *
+ */
+static int fcoe_sw_lport_config(struct fc_lport *lp)
+{
+ int i = 0;
+
+ lp->link_status = 0;
+ lp->max_retry_count = 3;
+ lp->e_d_tov = 2 * 1000; /* FC-FS default */
+ lp->r_a_tov = 2 * 2 * 1000;
+ lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+ FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+
+ /*
+ * allocate per cpu stats block
+ */
+ for_each_online_cpu(i)
+ lp->dev_stats[i] = kzalloc(sizeof(struct fcoe_dev_stats),
+ GFP_KERNEL);
+
+ /* lport fc_lport related configuration */
+ fc_lport_config(lp);
+
+ return 0;
+}
+
+/*
+ * fcoe_sw_netdev_config - sets up fcoe_softc for lport and network
+ * related properties
+ * @lp : ptr to the fc_lport
+ * @netdev : ptr to the associated netdevice struct
+ *
+ * Must be called after fcoe_sw_lport_config() as it will use lport mutex
+ *
+ * Returns : 0 for success
+ *
+ */
+static int fcoe_sw_netdev_config(struct fc_lport *lp, struct net_device *netdev)
+{
+ u32 mfs;
+ u64 wwnn, wwpn;
+ struct fcoe_softc *fc;
+ u8 flogi_maddr[ETH_ALEN];
+
+ /* Setup lport private data to point to fcoe softc */
+ fc = lport_priv(lp);
+ fc->lp = lp;
+ fc->real_dev = netdev;
+ fc->phys_dev = netdev;
+
+ /* Require support for get_pauseparam ethtool op. */
+ if (netdev->priv_flags & IFF_802_1Q_VLAN)
+ fc->phys_dev = vlan_dev_real_dev(netdev);
+
+ /* Do not support for bonding device */
+ if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
+ (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
+ (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
+ return -EOPNOTSUPP;
+ }
+
+ /*
+ * Determine max frame size based on underlying device and optional
+ * user-configured limit. If the MFS is too low, fcoe_link_ok()
+ * will return 0, so do this first.
+ */
+ mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
+ sizeof(struct fcoe_crc_eof));
+ if (fc_set_mfs(lp, mfs))
+ return -EINVAL;
+
+ lp->link_status = ~FC_PAUSE & ~FC_LINK_UP;
+ if (!fcoe_link_ok(lp))
+ lp->link_status |= FC_LINK_UP;
+
+ /* offload features support */
+ if (fc->real_dev->features & NETIF_F_SG)
+ lp->sg_supp = 1;
+
+
+ skb_queue_head_init(&fc->fcoe_pending_queue);
+
+ /* setup Source Mac Address */
+ memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
+ fc->real_dev->addr_len);
+
+ wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
+ fc_set_wwnn(lp, wwnn);
+ /* XXX - 3rd arg needs to be vlan id */
+ wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
+ fc_set_wwpn(lp, wwpn);
+
+ /*
+ * Add FCoE MAC address as second unicast MAC address
+ * or enter promiscuous mode if not capable of listening
+ * for multiple unicast MACs.
+ */
+ rtnl_lock();
+ memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+ dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
+ rtnl_unlock();
+
+ /*
+ * setup the receive function from ethernet driver
+ * on the ethertype for the given device
+ */
+ fc->fcoe_packet_type.func = fcoe_rcv;
+ fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+ fc->fcoe_packet_type.dev = fc->real_dev;
+ dev_add_pack(&fc->fcoe_packet_type);
+
+ return 0;
+}
+
+/*
+ * fcoe_sw_shost_config - sets up fc_lport->host
+ * @lp : ptr to the fc_lport
+ * @shost : ptr to the associated scsi host
+ * @dev : device associated to scsi host
+ *
+ * Must be called after fcoe_sw_lport_config) and fcoe_sw_netdev_config()
+ *
+ * Returns : 0 for success
+ *
+ */
+static int fcoe_sw_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
+ struct device *dev)
+{
+ int rc = 0;
+
+ /* lport scsi host config */
+ lp->host = shost;
+
+ lp->host->max_lun = FCOE_MAX_LUN;
+ lp->host->max_id = FCOE_MAX_FCP_TARGET;
+ lp->host->max_channel = 0;
+ lp->host->transportt = scsi_transport_fcoe_sw;
+
+ /* add the new host to the SCSI-ml */
+ rc = scsi_add_host(lp->host, dev);
+ if (rc) {
+ FC_DBG("fcoe_sw_shost_config:error on scsi_add_host\n");
+ return rc;
+ }
+ sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
+ FCOE_SW_NAME, FCOE_SW_VERSION,
+ fcoe_netdev(lp)->name);
+
+ return 0;
+}
+
+/*
+ * fcoe_sw_em_config - allocates em for this lport
+ * @lp: the port that em is to allocated for
+ *
+ * Returns : 0 on success
+ */
+static inline int fcoe_sw_em_config(struct fc_lport *lp)
+{
+ BUG_ON(lp->emp);
+
+ lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
+ FCOE_MIN_XID, FCOE_MAX_XID);
+ if (!lp->emp)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/*
+ * fcoe_sw_destroy - FCoE software HBA tear-down function
+ * @netdev: ptr to the associated net_device
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ */
+static int fcoe_sw_destroy(struct net_device *netdev)
+{
+ int cpu;
+ struct fc_lport *lp = NULL;
+ struct fcoe_softc *fc;
+ u8 flogi_maddr[ETH_ALEN];
+
+ BUG_ON(!netdev);
+
+ printk(KERN_DEBUG "fcoe_sw_destroy:interface on %s\n",
+ netdev->name);
+
+ lp = fcoe_hostlist_lookup(netdev);
+ if (!lp)
+ return -ENODEV;
+
+ fc = fcoe_softc(lp);
+
+ /* Logout of the fabric */
+ fc_fabric_logoff(lp);
+
+ /* Remove the instance from fcoe's list */
+ fcoe_hostlist_remove(lp);
+
+ /* Don't listen for Ethernet packets anymore */
+ dev_remove_pack(&fc->fcoe_packet_type);
+
+ /* Cleanup the fc_lport */
+ fc_lport_destroy(lp);
+ fc_fcp_destroy(lp);
+
+ /* Detach from the scsi-ml */
+ fc_remove_host(lp->host);
+ scsi_remove_host(lp->host);
+
+ /* There are no more rports or I/O, free the EM */
+ if (lp->emp)
+ fc_exch_mgr_free(lp->emp);
+
+ /* Delete secondary MAC addresses */
+ rtnl_lock();
+ memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+ dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
+ if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+ dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+ rtnl_unlock();
+
+ /* Free the per-CPU revieve threads */
+ fcoe_percpu_clean(lp);
+
+ /* Free existing skbs */
+ fcoe_clean_pending_queue(lp);
+
+ /* Free memory used by statistical counters */
+ for_each_online_cpu(cpu)
+ kfree(lp->dev_stats[cpu]);
+
+ /* Release the net_device and Scsi_Host */
+ dev_put(fc->real_dev);
+ scsi_host_put(lp->host);
+
+ return 0;
+}
+
+static struct libfc_function_template fcoe_sw_libfc_fcn_templ = {
+ .frame_send = fcoe_xmit,
+};
+
+/*
+ * fcoe_sw_create - this function creates the fcoe interface
+ * @netdev: pointer the associated netdevice
+ *
+ * Creates fc_lport struct and scsi_host for lport, configures lport
+ * and starts fabric login.
+ *
+ * Returns : 0 on success
+ */
+static int fcoe_sw_create(struct net_device *netdev)
+{
+ int rc;
+ struct fc_lport *lp = NULL;
+ struct fcoe_softc *fc;
+ struct Scsi_Host *shost;
+
+ BUG_ON(!netdev);
+
+ printk(KERN_DEBUG "fcoe_sw_create:interface on %s\n",
+ netdev->name);
+
+ lp = fcoe_hostlist_lookup(netdev);
+ if (lp)
+ return -EEXIST;
+
+ shost = fcoe_host_alloc(&fcoe_sw_shost_template,
+ sizeof(struct fcoe_softc));
+ if (!shost) {
+ FC_DBG("Could not allocate host structure\n");
+ return -ENOMEM;
+ }
+ lp = shost_priv(shost);
+ fc = lport_priv(lp);
+
+ /* configure fc_lport, e.g., em */
+ rc = fcoe_sw_lport_config(lp);
+ if (rc) {
+ FC_DBG("Could not configure lport\n");
+ goto out_host_put;
+ }
+
+ /* configure lport network properties */
+ rc = fcoe_sw_netdev_config(lp, netdev);
+ if (rc) {
+ FC_DBG("Could not configure netdev for lport\n");
+ goto out_host_put;
+ }
+
+ /* configure lport scsi host properties */
+ rc = fcoe_sw_shost_config(lp, shost, &netdev->dev);
+ if (rc) {
+ FC_DBG("Could not configure shost for lport\n");
+ goto out_host_put;
+ }
+
+ /* lport exch manager allocation */
+ rc = fcoe_sw_em_config(lp);
+ if (rc) {
+ FC_DBG("Could not configure em for lport\n");
+ goto out_host_put;
+ }
+
+ /* Initialize the library */
+ rc = fcoe_libfc_config(lp, &fcoe_sw_libfc_fcn_templ);
+ if (rc) {
+ FC_DBG("Could not configure libfc for lport!\n");
+ goto out_lp_destroy;
+ }
+
+ /* add to lports list */
+ fcoe_hostlist_add(lp);
+
+ lp->boot_time = jiffies;
+
+ fc_fabric_login(lp);
+
+ dev_hold(netdev);
+
+ return rc;
+
+out_lp_destroy:
+ fc_exch_mgr_free(lp->emp); /* Free the EM */
+out_host_put:
+ scsi_host_put(lp->host);
+ return rc;
+}
+
+/*
+ * fcoe_sw_match - the fcoe sw transport match function
+ *
+ * Returns : false always
+ */
+static bool fcoe_sw_match(struct net_device *netdev)
+{
+ /* FIXME - for sw transport, always return false */
+ return false;
+}
+
+/* the sw hba fcoe transport */
+struct fcoe_transport fcoe_sw_transport = {
+ .name = "fcoesw",
+ .create = fcoe_sw_create,
+ .destroy = fcoe_sw_destroy,
+ .match = fcoe_sw_match,
+ .vendor = 0x0,
+ .device = 0xffff,
+};
+
+/*
+ * fcoe_sw_init - registers fcoe_sw_transport
+ *
+ * Returns : 0 on success
+ */
+int __init fcoe_sw_init(void)
+{
+ /* attach to scsi transport */
+ scsi_transport_fcoe_sw =
+ fc_attach_transport(&fcoe_sw_transport_function);
+ if (!scsi_transport_fcoe_sw) {
+ printk(KERN_ERR "fcoe_sw_init:fc_attach_transport() failed\n");
+ return -ENODEV;
+ }
+ /* register sw transport */
+ fcoe_transport_register(&fcoe_sw_transport);
+ return 0;
+}
+
+/*
+ * fcoe_sw_exit - unregisters fcoe_sw_transport
+ *
+ * Returns : 0 on success
+ */
+int __exit fcoe_sw_exit(void)
+{
+ /* dettach the transport */
+ fc_release_transport(scsi_transport_fcoe_sw);
+ fcoe_transport_unregister(&fcoe_sw_transport);
+ return 0;
+}
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
new file mode 100644
index 000000000000..e419f486cdb3
--- /dev/null
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -0,0 +1,1510 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/kthread.h>
+#include <linux/crc32.h>
+#include <linux/cpu.h>
+#include <linux/fs.h>
+#include <linux/sysfs.h>
+#include <linux/ctype.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+#include <net/rtnetlink.h>
+
+#include <scsi/fc/fc_encaps.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
+#include <scsi/libfcoe.h>
+#include <scsi/fc_transport_fcoe.h>
+
+static int debug_fcoe;
+
+#define FCOE_MAX_QUEUE_DEPTH 256
+
+/* destination address mode */
+#define FCOE_GW_ADDR_MODE 0x00
+#define FCOE_FCOUI_ADDR_MODE 0x01
+
+#define FCOE_WORD_TO_BYTE 4
+
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("FCoE");
+MODULE_LICENSE("GPL");
+
+/* fcoe host list */
+LIST_HEAD(fcoe_hostlist);
+DEFINE_RWLOCK(fcoe_hostlist_lock);
+DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
+struct fcoe_percpu_s *fcoe_percpu[NR_CPUS];
+
+
+/* Function Prototyes */
+static int fcoe_check_wait_queue(struct fc_lport *);
+static void fcoe_insert_wait_queue_head(struct fc_lport *, struct sk_buff *);
+static void fcoe_insert_wait_queue(struct fc_lport *, struct sk_buff *);
+static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
+#ifdef CONFIG_HOTPLUG_CPU
+static int fcoe_cpu_callback(struct notifier_block *, ulong, void *);
+#endif /* CONFIG_HOTPLUG_CPU */
+static int fcoe_device_notification(struct notifier_block *, ulong, void *);
+static void fcoe_dev_setup(void);
+static void fcoe_dev_cleanup(void);
+
+/* notification function from net device */
+static struct notifier_block fcoe_notifier = {
+ .notifier_call = fcoe_device_notification,
+};
+
+
+#ifdef CONFIG_HOTPLUG_CPU
+static struct notifier_block fcoe_cpu_notifier = {
+ .notifier_call = fcoe_cpu_callback,
+};
+
+/**
+ * fcoe_create_percpu_data - creates the associated cpu data
+ * @cpu: index for the cpu where fcoe cpu data will be created
+ *
+ * create percpu stats block, from cpu add notifier
+ *
+ * Returns: none
+ **/
+static void fcoe_create_percpu_data(int cpu)
+{
+ struct fc_lport *lp;
+ struct fcoe_softc *fc;
+
+ write_lock_bh(&fcoe_hostlist_lock);
+ list_for_each_entry(fc, &fcoe_hostlist, list) {
+ lp = fc->lp;
+ if (lp->dev_stats[cpu] == NULL)
+ lp->dev_stats[cpu] =
+ kzalloc(sizeof(struct fcoe_dev_stats),
+ GFP_KERNEL);
+ }
+ write_unlock_bh(&fcoe_hostlist_lock);
+}
+
+/**
+ * fcoe_destroy_percpu_data - destroys the associated cpu data
+ * @cpu: index for the cpu where fcoe cpu data will destroyed
+ *
+ * destroy percpu stats block called by cpu add/remove notifier
+ *
+ * Retuns: none
+ **/
+static void fcoe_destroy_percpu_data(int cpu)
+{
+ struct fc_lport *lp;
+ struct fcoe_softc *fc;
+
+ write_lock_bh(&fcoe_hostlist_lock);
+ list_for_each_entry(fc, &fcoe_hostlist, list) {
+ lp = fc->lp;
+ kfree(lp->dev_stats[cpu]);
+ lp->dev_stats[cpu] = NULL;
+ }
+ write_unlock_bh(&fcoe_hostlist_lock);
+}
+
+/**
+ * fcoe_cpu_callback - fcoe cpu hotplug event callback
+ * @nfb: callback data block
+ * @action: event triggering the callback
+ * @hcpu: index for the cpu of this event
+ *
+ * this creates or destroys per cpu data for fcoe
+ *
+ * Returns NOTIFY_OK always.
+ **/
+static int fcoe_cpu_callback(struct notifier_block *nfb, unsigned long action,
+ void *hcpu)
+{
+ unsigned int cpu = (unsigned long)hcpu;
+
+ switch (action) {
+ case CPU_ONLINE:
+ fcoe_create_percpu_data(cpu);
+ break;
+ case CPU_DEAD:
+ fcoe_destroy_percpu_data(cpu);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+/**
+ * fcoe_rcv - this is the fcoe receive function called by NET_RX_SOFTIRQ
+ * @skb: the receive skb
+ * @dev: associated net device
+ * @ptype: context
+ * @odldev: last device
+ *
+ * this function will receive the packet and build fc frame and pass it up
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *ptype, struct net_device *olddev)
+{
+ struct fc_lport *lp;
+ struct fcoe_rcv_info *fr;
+ struct fcoe_softc *fc;
+ struct fcoe_dev_stats *stats;
+ struct fc_frame_header *fh;
+ unsigned short oxid;
+ int cpu_idx;
+ struct fcoe_percpu_s *fps;
+
+ fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
+ lp = fc->lp;
+ if (unlikely(lp == NULL)) {
+ FC_DBG("cannot find hba structure");
+ goto err2;
+ }
+
+ if (unlikely(debug_fcoe)) {
+ FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p "
+ "end:%p sum:%d dev:%s", skb->len, skb->data_len,
+ skb->head, skb->data, skb_tail_pointer(skb),
+ skb_end_pointer(skb), skb->csum,
+ skb->dev ? skb->dev->name : "<NULL>");
+
+ }
+
+ /* check for FCOE packet type */
+ if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
+ FC_DBG("wrong FC type frame");
+ goto err;
+ }
+
+ /*
+ * Check for minimum frame length, and make sure required FCoE
+ * and FC headers are pulled into the linear data area.
+ */
+ if (unlikely((skb->len < FCOE_MIN_FRAME) ||
+ !pskb_may_pull(skb, FCOE_HEADER_LEN)))
+ goto err;
+
+ skb_set_transport_header(skb, sizeof(struct fcoe_hdr));
+ fh = (struct fc_frame_header *) skb_transport_header(skb);
+
+ oxid = ntohs(fh->fh_ox_id);
+
+ fr = fcoe_dev_from_skb(skb);
+ fr->fr_dev = lp;
+ fr->ptype = ptype;
+ cpu_idx = 0;
+#ifdef CONFIG_SMP
+ /*
+ * The incoming frame exchange id(oxid) is ANDed with num of online
+ * cpu bits to get cpu_idx and then this cpu_idx is used for selecting
+ * a per cpu kernel thread from fcoe_percpu. In case the cpu is
+ * offline or no kernel thread for derived cpu_idx then cpu_idx is
+ * initialize to first online cpu index.
+ */
+ cpu_idx = oxid & (num_online_cpus() - 1);
+ if (!fcoe_percpu[cpu_idx] || !cpu_online(cpu_idx))
+ cpu_idx = first_cpu(cpu_online_map);
+#endif
+ fps = fcoe_percpu[cpu_idx];
+
+ spin_lock_bh(&fps->fcoe_rx_list.lock);
+ __skb_queue_tail(&fps->fcoe_rx_list, skb);
+ if (fps->fcoe_rx_list.qlen == 1)
+ wake_up_process(fps->thread);
+
+ spin_unlock_bh(&fps->fcoe_rx_list.lock);
+
+ return 0;
+err:
+#ifdef CONFIG_SMP
+ stats = lp->dev_stats[smp_processor_id()];
+#else
+ stats = lp->dev_stats[0];
+#endif
+ if (stats)
+ stats->ErrorFrames++;
+
+err2:
+ kfree_skb(skb);
+ return -1;
+}
+EXPORT_SYMBOL_GPL(fcoe_rcv);
+
+/**
+ * fcoe_start_io - pass to netdev to start xmit for fcoe
+ * @skb: the skb to be xmitted
+ *
+ * Returns: 0 for success
+ **/
+static inline int fcoe_start_io(struct sk_buff *skb)
+{
+ int rc;
+
+ skb_get(skb);
+ rc = dev_queue_xmit(skb);
+ if (rc != 0)
+ return rc;
+ kfree_skb(skb);
+ return 0;
+}
+
+/**
+ * fcoe_get_paged_crc_eof - in case we need alloc a page for crc_eof
+ * @skb: the skb to be xmitted
+ * @tlen: total len
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
+{
+ struct fcoe_percpu_s *fps;
+ struct page *page;
+ int cpu_idx;
+
+ cpu_idx = get_cpu();
+ fps = fcoe_percpu[cpu_idx];
+ page = fps->crc_eof_page;
+ if (!page) {
+ page = alloc_page(GFP_ATOMIC);
+ if (!page) {
+ put_cpu();
+ return -ENOMEM;
+ }
+ fps->crc_eof_page = page;
+ WARN_ON(fps->crc_eof_offset != 0);
+ }
+
+ get_page(page);
+ skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page,
+ fps->crc_eof_offset, tlen);
+ skb->len += tlen;
+ skb->data_len += tlen;
+ skb->truesize += tlen;
+ fps->crc_eof_offset += sizeof(struct fcoe_crc_eof);
+
+ if (fps->crc_eof_offset >= PAGE_SIZE) {
+ fps->crc_eof_page = NULL;
+ fps->crc_eof_offset = 0;
+ put_page(page);
+ }
+ put_cpu();
+ return 0;
+}
+
+/**
+ * fcoe_fc_crc - calculates FC CRC in this fcoe skb
+ * @fp: the fc_frame containg data to be checksummed
+ *
+ * This uses crc32() to calculate the crc for fc frame
+ * Return : 32 bit crc
+ *
+ **/
+u32 fcoe_fc_crc(struct fc_frame *fp)
+{
+ struct sk_buff *skb = fp_skb(fp);
+ struct skb_frag_struct *frag;
+ unsigned char *data;
+ unsigned long off, len, clen;
+ u32 crc;
+ unsigned i;
+
+ crc = crc32(~0, skb->data, skb_headlen(skb));
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ frag = &skb_shinfo(skb)->frags[i];
+ off = frag->page_offset;
+ len = frag->size;
+ while (len > 0) {
+ clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK));
+ data = kmap_atomic(frag->page + (off >> PAGE_SHIFT),
+ KM_SKB_DATA_SOFTIRQ);
+ crc = crc32(crc, data + (off & ~PAGE_MASK), clen);
+ kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ);
+ off += clen;
+ len -= clen;
+ }
+ }
+ return crc;
+}
+EXPORT_SYMBOL_GPL(fcoe_fc_crc);
+
+/**
+ * fcoe_xmit - FCoE frame transmit function
+ * @lp: the associated local port
+ * @fp: the fc_frame to be transmitted
+ *
+ * Return : 0 for success
+ *
+ **/
+int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
+{
+ int wlen, rc = 0;
+ u32 crc;
+ struct ethhdr *eh;
+ struct fcoe_crc_eof *cp;
+ struct sk_buff *skb;
+ struct fcoe_dev_stats *stats;
+ struct fc_frame_header *fh;
+ unsigned int hlen; /* header length implies the version */
+ unsigned int tlen; /* trailer length */
+ unsigned int elen; /* eth header, may include vlan */
+ int flogi_in_progress = 0;
+ struct fcoe_softc *fc;
+ u8 sof, eof;
+ struct fcoe_hdr *hp;
+
+ WARN_ON((fr_len(fp) % sizeof(u32)) != 0);
+
+ fc = fcoe_softc(lp);
+ /*
+ * if it is a flogi then we need to learn gw-addr
+ * and my own fcid
+ */
+ fh = fc_frame_header_get(fp);
+ if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
+ if (fc_frame_payload_op(fp) == ELS_FLOGI) {
+ fc->flogi_oxid = ntohs(fh->fh_ox_id);
+ fc->address_mode = FCOE_FCOUI_ADDR_MODE;
+ fc->flogi_progress = 1;
+ flogi_in_progress = 1;
+ } else if (fc->flogi_progress && ntoh24(fh->fh_s_id) != 0) {
+ /*
+ * Here we must've gotten an SID by accepting an FLOGI
+ * from a point-to-point connection. Switch to using
+ * the source mac based on the SID. The destination
+ * MAC in this case would have been set by receving the
+ * FLOGI.
+ */
+ fc_fcoe_set_mac(fc->data_src_addr, fh->fh_s_id);
+ fc->flogi_progress = 0;
+ }
+ }
+
+ skb = fp_skb(fp);
+ sof = fr_sof(fp);
+ eof = fr_eof(fp);
+
+ elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ?
+ sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr);
+ hlen = sizeof(struct fcoe_hdr);
+ tlen = sizeof(struct fcoe_crc_eof);
+ wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
+
+ /* crc offload */
+ if (likely(lp->crc_offload)) {
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ skb->csum_start = skb_headroom(skb);
+ skb->csum_offset = skb->len;
+ crc = 0;
+ } else {
+ skb->ip_summed = CHECKSUM_NONE;
+ crc = fcoe_fc_crc(fp);
+ }
+
+ /* copy fc crc and eof to the skb buff */
+ if (skb_is_nonlinear(skb)) {
+ skb_frag_t *frag;
+ if (fcoe_get_paged_crc_eof(skb, tlen)) {
+ kfree(skb);
+ return -ENOMEM;
+ }
+ frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
+ cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
+ + frag->page_offset;
+ } else {
+ cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
+ }
+
+ memset(cp, 0, sizeof(*cp));
+ cp->fcoe_eof = eof;
+ cp->fcoe_crc32 = cpu_to_le32(~crc);
+
+ if (skb_is_nonlinear(skb)) {
+ kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ);
+ cp = NULL;
+ }
+
+ /* adjust skb netowrk/transport offsets to match mac/fcoe/fc */
+ skb_push(skb, elen + hlen);
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+ skb->mac_len = elen;
+ skb->protocol = htons(ETH_P_802_3);
+ skb->dev = fc->real_dev;
+
+ /* fill up mac and fcoe headers */
+ eh = eth_hdr(skb);
+ eh->h_proto = htons(ETH_P_FCOE);
+ if (fc->address_mode == FCOE_FCOUI_ADDR_MODE)
+ fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
+ else
+ /* insert GW address */
+ memcpy(eh->h_dest, fc->dest_addr, ETH_ALEN);
+
+ if (unlikely(flogi_in_progress))
+ memcpy(eh->h_source, fc->ctl_src_addr, ETH_ALEN);
+ else
+ memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN);
+
+ hp = (struct fcoe_hdr *)(eh + 1);
+ memset(hp, 0, sizeof(*hp));
+ if (FC_FCOE_VER)
+ FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
+ hp->fcoe_sof = sof;
+
+ /* update tx stats: regardless if LLD fails */
+ stats = lp->dev_stats[smp_processor_id()];
+ if (stats) {
+ stats->TxFrames++;
+ stats->TxWords += wlen;
+ }
+
+ /* send down to lld */
+ fr_dev(fp) = lp;
+ if (fc->fcoe_pending_queue.qlen)
+ rc = fcoe_check_wait_queue(lp);
+
+ if (rc == 0)
+ rc = fcoe_start_io(skb);
+
+ if (rc) {
+ fcoe_insert_wait_queue(lp, skb);
+ if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+ fc_pause(lp);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_xmit);
+
+/*
+ * fcoe_percpu_receive_thread - recv thread per cpu
+ * @arg: ptr to the fcoe per cpu struct
+ *
+ * Return: 0 for success
+ *
+ */
+int fcoe_percpu_receive_thread(void *arg)
+{
+ struct fcoe_percpu_s *p = arg;
+ u32 fr_len;
+ struct fc_lport *lp;
+ struct fcoe_rcv_info *fr;
+ struct fcoe_dev_stats *stats;
+ struct fc_frame_header *fh;
+ struct sk_buff *skb;
+ struct fcoe_crc_eof crc_eof;
+ struct fc_frame *fp;
+ u8 *mac = NULL;
+ struct fcoe_softc *fc;
+ struct fcoe_hdr *hp;
+
+ set_user_nice(current, 19);
+
+ while (!kthread_should_stop()) {
+
+ spin_lock_bh(&p->fcoe_rx_list.lock);
+ while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock_bh(&p->fcoe_rx_list.lock);
+ schedule();
+ set_current_state(TASK_RUNNING);
+ if (kthread_should_stop())
+ return 0;
+ spin_lock_bh(&p->fcoe_rx_list.lock);
+ }
+ spin_unlock_bh(&p->fcoe_rx_list.lock);
+ fr = fcoe_dev_from_skb(skb);
+ lp = fr->fr_dev;
+ if (unlikely(lp == NULL)) {
+ FC_DBG("invalid HBA Structure");
+ kfree_skb(skb);
+ continue;
+ }
+
+ stats = lp->dev_stats[smp_processor_id()];
+
+ if (unlikely(debug_fcoe)) {
+ FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p "
+ "tail:%p end:%p sum:%d dev:%s",
+ skb->len, skb->data_len,
+ skb->head, skb->data, skb_tail_pointer(skb),
+ skb_end_pointer(skb), skb->csum,
+ skb->dev ? skb->dev->name : "<NULL>");
+ }
+
+ /*
+ * Save source MAC address before discarding header.
+ */
+ fc = lport_priv(lp);
+ if (unlikely(fc->flogi_progress))
+ mac = eth_hdr(skb)->h_source;
+
+ if (skb_is_nonlinear(skb))
+ skb_linearize(skb); /* not ideal */
+
+ /*
+ * Frame length checks and setting up the header pointers
+ * was done in fcoe_rcv already.
+ */
+ hp = (struct fcoe_hdr *) skb_network_header(skb);
+ fh = (struct fc_frame_header *) skb_transport_header(skb);
+
+ if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
+ if (stats) {
+ if (stats->ErrorFrames < 5)
+ FC_DBG("unknown FCoE version %x",
+ FC_FCOE_DECAPS_VER(hp));
+ stats->ErrorFrames++;
+ }
+ kfree_skb(skb);
+ continue;
+ }
+
+ skb_pull(skb, sizeof(struct fcoe_hdr));
+ fr_len = skb->len - sizeof(struct fcoe_crc_eof);
+
+ if (stats) {
+ stats->RxFrames++;
+ stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
+ }
+
+ fp = (struct fc_frame *)skb;
+ fc_frame_init(fp);
+ fr_dev(fp) = lp;
+ fr_sof(fp) = hp->fcoe_sof;
+
+ /* Copy out the CRC and EOF trailer for access */
+ if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
+ kfree_skb(skb);
+ continue;
+ }
+ fr_eof(fp) = crc_eof.fcoe_eof;
+ fr_crc(fp) = crc_eof.fcoe_crc32;
+ if (pskb_trim(skb, fr_len)) {
+ kfree_skb(skb);
+ continue;
+ }
+
+ /*
+ * We only check CRC if no offload is available and if it is
+ * it's solicited data, in which case, the FCP layer would
+ * check it during the copy.
+ */
+ if (lp->crc_offload)
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ else
+ fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
+
+ fh = fc_frame_header_get(fp);
+ if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
+ fh->fh_type == FC_TYPE_FCP) {
+ fc_exch_recv(lp, lp->emp, fp);
+ continue;
+ }
+ if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+ if (le32_to_cpu(fr_crc(fp)) !=
+ ~crc32(~0, skb->data, fr_len)) {
+ if (debug_fcoe || stats->InvalidCRCCount < 5)
+ printk(KERN_WARNING "fcoe: dropping "
+ "frame with CRC error\n");
+ stats->InvalidCRCCount++;
+ stats->ErrorFrames++;
+ fc_frame_free(fp);
+ continue;
+ }
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ }
+ /* non flogi and non data exchanges are handled here */
+ if (unlikely(fc->flogi_progress))
+ fcoe_recv_flogi(fc, fp, mac);
+ fc_exch_recv(lp, lp->emp, fp);
+ }
+ return 0;
+}
+
+/**
+ * fcoe_recv_flogi - flogi receive function
+ * @fc: associated fcoe_softc
+ * @fp: the recieved frame
+ * @sa: the source address of this flogi
+ *
+ * This is responsible to parse the flogi response and sets the corresponding
+ * mac address for the initiator, eitehr OUI based or GW based.
+ *
+ * Returns: none
+ **/
+static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa)
+{
+ struct fc_frame_header *fh;
+ u8 op;
+
+ fh = fc_frame_header_get(fp);
+ if (fh->fh_type != FC_TYPE_ELS)
+ return;
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
+ fc->flogi_oxid == ntohs(fh->fh_ox_id)) {
+ /*
+ * FLOGI accepted.
+ * If the src mac addr is FC_OUI-based, then we mark the
+ * address_mode flag to use FC_OUI-based Ethernet DA.
+ * Otherwise we use the FCoE gateway addr
+ */
+ if (!compare_ether_addr(sa, (u8[6]) FC_FCOE_FLOGI_MAC)) {
+ fc->address_mode = FCOE_FCOUI_ADDR_MODE;
+ } else {
+ memcpy(fc->dest_addr, sa, ETH_ALEN);
+ fc->address_mode = FCOE_GW_ADDR_MODE;
+ }
+
+ /*
+ * Remove any previously-set unicast MAC filter.
+ * Add secondary FCoE MAC address filter for our OUI.
+ */
+ rtnl_lock();
+ if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+ dev_unicast_delete(fc->real_dev, fc->data_src_addr,
+ ETH_ALEN);
+ fc_fcoe_set_mac(fc->data_src_addr, fh->fh_d_id);
+ dev_unicast_add(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+ rtnl_unlock();
+
+ fc->flogi_progress = 0;
+ } else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
+ /*
+ * Save source MAC for point-to-point responses.
+ */
+ memcpy(fc->dest_addr, sa, ETH_ALEN);
+ fc->address_mode = FCOE_GW_ADDR_MODE;
+ }
+}
+
+/**
+ * fcoe_watchdog - fcoe timer callback
+ * @vp:
+ *
+ * This checks the pending queue length for fcoe and put fcoe to be paused state
+ * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the
+ * fcoe_hostlist.
+ *
+ * Returns: 0 for success
+ **/
+void fcoe_watchdog(ulong vp)
+{
+ struct fc_lport *lp;
+ struct fcoe_softc *fc;
+ int paused = 0;
+
+ read_lock(&fcoe_hostlist_lock);
+ list_for_each_entry(fc, &fcoe_hostlist, list) {
+ lp = fc->lp;
+ if (lp) {
+ if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+ paused = 1;
+ if (fcoe_check_wait_queue(lp) < FCOE_MAX_QUEUE_DEPTH) {
+ if (paused)
+ fc_unpause(lp);
+ }
+ }
+ }
+ read_unlock(&fcoe_hostlist_lock);
+
+ fcoe_timer.expires = jiffies + (1 * HZ);
+ add_timer(&fcoe_timer);
+}
+
+
+/**
+ * fcoe_check_wait_queue - put the skb into fcoe pending xmit queue
+ * @lp: the fc_port for this skb
+ * @skb: the associated skb to be xmitted
+ *
+ * This empties the wait_queue, dequeue the head of the wait_queue queue
+ * and calls fcoe_start_io() for each packet, if all skb have been
+ * transmitted, return 0 if a error occurs, then restore wait_queue and
+ * try again later.
+ *
+ * The wait_queue is used when the skb transmit fails. skb will go
+ * in the wait_queue which will be emptied by the time function OR
+ * by the next skb transmit.
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_check_wait_queue(struct fc_lport *lp)
+{
+ int rc, unpause = 0;
+ int paused = 0;
+ struct sk_buff *skb;
+ struct fcoe_softc *fc;
+
+ fc = fcoe_softc(lp);
+ spin_lock_bh(&fc->fcoe_pending_queue.lock);
+
+ /*
+ * is this interface paused?
+ */
+ if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+ paused = 1;
+ if (fc->fcoe_pending_queue.qlen) {
+ while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
+ spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+ rc = fcoe_start_io(skb);
+ if (rc) {
+ fcoe_insert_wait_queue_head(lp, skb);
+ return rc;
+ }
+ spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ }
+ if (fc->fcoe_pending_queue.qlen < FCOE_MAX_QUEUE_DEPTH)
+ unpause = 1;
+ }
+ spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+ if ((unpause) && (paused))
+ fc_unpause(lp);
+ return fc->fcoe_pending_queue.qlen;
+}
+
+/**
+ * fcoe_insert_wait_queue_head - puts skb to fcoe pending queue head
+ * @lp: the fc_port for this skb
+ * @skb: the associated skb to be xmitted
+ *
+ * Returns: none
+ **/
+static void fcoe_insert_wait_queue_head(struct fc_lport *lp,
+ struct sk_buff *skb)
+{
+ struct fcoe_softc *fc;
+
+ fc = fcoe_softc(lp);
+ spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ __skb_queue_head(&fc->fcoe_pending_queue, skb);
+ spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+}
+
+/**
+ * fcoe_insert_wait_queue - put the skb into fcoe pending queue tail
+ * @lp: the fc_port for this skb
+ * @skb: the associated skb to be xmitted
+ *
+ * Returns: none
+ **/
+static void fcoe_insert_wait_queue(struct fc_lport *lp,
+ struct sk_buff *skb)
+{
+ struct fcoe_softc *fc;
+
+ fc = fcoe_softc(lp);
+ spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ __skb_queue_tail(&fc->fcoe_pending_queue, skb);
+ spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+}
+
+/**
+ * fcoe_dev_setup - setup link change notification interface
+ *
+ **/
+static void fcoe_dev_setup(void)
+{
+ /*
+ * here setup a interface specific wd time to
+ * monitor the link state
+ */
+ register_netdevice_notifier(&fcoe_notifier);
+}
+
+/**
+ * fcoe_dev_setup - cleanup link change notification interface
+ **/
+static void fcoe_dev_cleanup(void)
+{
+ unregister_netdevice_notifier(&fcoe_notifier);
+}
+
+/**
+ * fcoe_device_notification - netdev event notification callback
+ * @notifier: context of the notification
+ * @event: type of event
+ * @ptr: fixed array for output parsed ifname
+ *
+ * This function is called by the ethernet driver in case of link change event
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_device_notification(struct notifier_block *notifier,
+ ulong event, void *ptr)
+{
+ struct fc_lport *lp = NULL;
+ struct net_device *real_dev = ptr;
+ struct fcoe_softc *fc;
+ struct fcoe_dev_stats *stats;
+ u16 new_status;
+ u32 mfs;
+ int rc = NOTIFY_OK;
+
+ read_lock(&fcoe_hostlist_lock);
+ list_for_each_entry(fc, &fcoe_hostlist, list) {
+ if (fc->real_dev == real_dev) {
+ lp = fc->lp;
+ break;
+ }
+ }
+ read_unlock(&fcoe_hostlist_lock);
+ if (lp == NULL) {
+ rc = NOTIFY_DONE;
+ goto out;
+ }
+
+ new_status = lp->link_status;
+ switch (event) {
+ case NETDEV_DOWN:
+ case NETDEV_GOING_DOWN:
+ new_status &= ~FC_LINK_UP;
+ break;
+ case NETDEV_UP:
+ case NETDEV_CHANGE:
+ new_status &= ~FC_LINK_UP;
+ if (!fcoe_link_ok(lp))
+ new_status |= FC_LINK_UP;
+ break;
+ case NETDEV_CHANGEMTU:
+ mfs = fc->real_dev->mtu -
+ (sizeof(struct fcoe_hdr) +
+ sizeof(struct fcoe_crc_eof));
+ if (mfs >= FC_MIN_MAX_FRAME)
+ fc_set_mfs(lp, mfs);
+ new_status &= ~FC_LINK_UP;
+ if (!fcoe_link_ok(lp))
+ new_status |= FC_LINK_UP;
+ break;
+ case NETDEV_REGISTER:
+ break;
+ default:
+ FC_DBG("unknown event %ld call", event);
+ }
+ if (lp->link_status != new_status) {
+ if ((new_status & FC_LINK_UP) == FC_LINK_UP)
+ fc_linkup(lp);
+ else {
+ stats = lp->dev_stats[smp_processor_id()];
+ if (stats)
+ stats->LinkFailureCount++;
+ fc_linkdown(lp);
+ fcoe_clean_pending_queue(lp);
+ }
+ }
+out:
+ return rc;
+}
+
+/**
+ * fcoe_if_to_netdev - parse a name buffer to get netdev
+ * @ifname: fixed array for output parsed ifname
+ * @buffer: incoming buffer to be copied
+ *
+ * Returns: NULL or ptr to netdeive
+ **/
+static struct net_device *fcoe_if_to_netdev(const char *buffer)
+{
+ char *cp;
+ char ifname[IFNAMSIZ + 2];
+
+ if (buffer) {
+ strlcpy(ifname, buffer, IFNAMSIZ);
+ cp = ifname + strlen(ifname);
+ while (--cp >= ifname && *cp == '\n')
+ *cp = '\0';
+ return dev_get_by_name(&init_net, ifname);
+ }
+ return NULL;
+}
+
+/**
+ * fcoe_netdev_to_module_owner - finds out the nic drive moddule of the netdev
+ * @netdev: the target netdev
+ *
+ * Returns: ptr to the struct module, NULL for failure
+ **/
+static struct module *fcoe_netdev_to_module_owner(
+ const struct net_device *netdev)
+{
+ struct device *dev;
+
+ if (!netdev)
+ return NULL;
+
+ dev = netdev->dev.parent;
+ if (!dev)
+ return NULL;
+
+ if (!dev->driver)
+ return NULL;
+
+ return dev->driver->owner;
+}
+
+/**
+ * fcoe_ethdrv_get - holds the nic driver module by try_module_get() for
+ * the corresponding netdev.
+ * @netdev: the target netdev
+ *
+ * Returns: 0 for succsss
+ **/
+static int fcoe_ethdrv_get(const struct net_device *netdev)
+{
+ struct module *owner;
+
+ owner = fcoe_netdev_to_module_owner(netdev);
+ if (owner) {
+ printk(KERN_DEBUG "fcoe:hold driver module %s for %s\n",
+ module_name(owner), netdev->name);
+ return try_module_get(owner);
+ }
+ return -ENODEV;
+}
+
+/**
+ * fcoe_ethdrv_get - releases the nic driver module by module_put for
+ * the corresponding netdev.
+ * @netdev: the target netdev
+ *
+ * Returns: 0 for succsss
+ **/
+static int fcoe_ethdrv_put(const struct net_device *netdev)
+{
+ struct module *owner;
+
+ owner = fcoe_netdev_to_module_owner(netdev);
+ if (owner) {
+ printk(KERN_DEBUG "fcoe:release driver module %s for %s\n",
+ module_name(owner), netdev->name);
+ module_put(owner);
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/**
+ * fcoe_destroy- handles the destroy from sysfs
+ * @buffer: expcted to be a eth if name
+ * @kp: associated kernel param
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
+{
+ int rc;
+ struct net_device *netdev;
+
+ netdev = fcoe_if_to_netdev(buffer);
+ if (!netdev) {
+ rc = -ENODEV;
+ goto out_nodev;
+ }
+ /* look for existing lport */
+ if (!fcoe_hostlist_lookup(netdev)) {
+ rc = -ENODEV;
+ goto out_putdev;
+ }
+ /* pass to transport */
+ rc = fcoe_transport_release(netdev);
+ if (rc) {
+ printk(KERN_ERR "fcoe: fcoe_transport_release(%s) failed\n",
+ netdev->name);
+ rc = -EIO;
+ goto out_putdev;
+ }
+ fcoe_ethdrv_put(netdev);
+ rc = 0;
+out_putdev:
+ dev_put(netdev);
+out_nodev:
+ return rc;
+}
+
+/**
+ * fcoe_create - handles the create call from sysfs
+ * @buffer: expcted to be a eth if name
+ * @kp: associated kernel param
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_create(const char *buffer, struct kernel_param *kp)
+{
+ int rc;
+ struct net_device *netdev;
+
+ netdev = fcoe_if_to_netdev(buffer);
+ if (!netdev) {
+ rc = -ENODEV;
+ goto out_nodev;
+ }
+ /* look for existing lport */
+ if (fcoe_hostlist_lookup(netdev)) {
+ rc = -EEXIST;
+ goto out_putdev;
+ }
+ fcoe_ethdrv_get(netdev);
+
+ /* pass to transport */
+ rc = fcoe_transport_attach(netdev);
+ if (rc) {
+ printk(KERN_ERR "fcoe: fcoe_transport_attach(%s) failed\n",
+ netdev->name);
+ fcoe_ethdrv_put(netdev);
+ rc = -EIO;
+ goto out_putdev;
+ }
+ rc = 0;
+out_putdev:
+ dev_put(netdev);
+out_nodev:
+ return rc;
+}
+
+module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(create, "string");
+MODULE_PARM_DESC(create, "Create fcoe port using net device passed in.");
+module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(destroy, "string");
+MODULE_PARM_DESC(destroy, "Destroy fcoe port");
+
+/*
+ * fcoe_link_ok - check if link is ok for the fc_lport
+ * @lp: ptr to the fc_lport
+ *
+ * Any permanently-disqualifying conditions have been previously checked.
+ * This also updates the speed setting, which may change with link for 100/1000.
+ *
+ * This function should probably be checking for PAUSE support at some point
+ * in the future. Currently Per-priority-pause is not determinable using
+ * ethtool, so we shouldn't be restrictive until that problem is resolved.
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ *
+ */
+int fcoe_link_ok(struct fc_lport *lp)
+{
+ struct fcoe_softc *fc = fcoe_softc(lp);
+ struct net_device *dev = fc->real_dev;
+ struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+ int rc = 0;
+
+ if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) {
+ dev = fc->phys_dev;
+ if (dev->ethtool_ops->get_settings) {
+ dev->ethtool_ops->get_settings(dev, &ecmd);
+ lp->link_supported_speeds &=
+ ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
+ if (ecmd.supported & (SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full))
+ lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
+ if (ecmd.supported & SUPPORTED_10000baseT_Full)
+ lp->link_supported_speeds |=
+ FC_PORTSPEED_10GBIT;
+ if (ecmd.speed == SPEED_1000)
+ lp->link_speed = FC_PORTSPEED_1GBIT;
+ if (ecmd.speed == SPEED_10000)
+ lp->link_speed = FC_PORTSPEED_10GBIT;
+ }
+ } else
+ rc = -1;
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(fcoe_link_ok);
+
+/*
+ * fcoe_percpu_clean - frees skb of the corresponding lport from the per
+ * cpu queue.
+ * @lp: the fc_lport
+ */
+void fcoe_percpu_clean(struct fc_lport *lp)
+{
+ int idx;
+ struct fcoe_percpu_s *pp;
+ struct fcoe_rcv_info *fr;
+ struct sk_buff_head *list;
+ struct sk_buff *skb, *next;
+ struct sk_buff *head;
+
+ for (idx = 0; idx < NR_CPUS; idx++) {
+ if (fcoe_percpu[idx]) {
+ pp = fcoe_percpu[idx];
+ spin_lock_bh(&pp->fcoe_rx_list.lock);
+ list = &pp->fcoe_rx_list;
+ head = list->next;
+ for (skb = head; skb != (struct sk_buff *)list;
+ skb = next) {
+ next = skb->next;
+ fr = fcoe_dev_from_skb(skb);
+ if (fr->fr_dev == lp) {
+ __skb_unlink(skb, list);
+ kfree_skb(skb);
+ }
+ }
+ spin_unlock_bh(&pp->fcoe_rx_list.lock);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(fcoe_percpu_clean);
+
+/**
+ * fcoe_clean_pending_queue - dequeue skb and free it
+ * @lp: the corresponding fc_lport
+ *
+ * Returns: none
+ **/
+void fcoe_clean_pending_queue(struct fc_lport *lp)
+{
+ struct fcoe_softc *fc = lport_priv(lp);
+ struct sk_buff *skb;
+
+ spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
+ spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+ kfree_skb(skb);
+ spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ }
+ spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+}
+EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
+
+/**
+ * libfc_host_alloc - allocate a Scsi_Host with room for the fc_lport
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ * TODO - to libfc?
+ */
+static inline struct Scsi_Host *libfc_host_alloc(
+ struct scsi_host_template *sht, int priv_size)
+{
+ return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
+}
+
+/**
+ * fcoe_host_alloc - allocate a Scsi_Host with room for the fcoe_softc
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ */
+struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *sht, int priv_size)
+{
+ return libfc_host_alloc(sht, sizeof(struct fcoe_softc) + priv_size);
+}
+EXPORT_SYMBOL_GPL(fcoe_host_alloc);
+
+/*
+ * fcoe_reset - resets the fcoe
+ * @shost: shost the reset is from
+ *
+ * Returns: always 0
+ */
+int fcoe_reset(struct Scsi_Host *shost)
+{
+ struct fc_lport *lport = shost_priv(shost);
+ fc_lport_reset(lport);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_reset);
+
+/*
+ * fcoe_wwn_from_mac - converts 48-bit IEEE MAC address to 64-bit FC WWN.
+ * @mac: mac address
+ * @scheme: check port
+ * @port: port indicator for converting
+ *
+ * Returns: u64 fc world wide name
+ */
+u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
+ unsigned int scheme, unsigned int port)
+{
+ u64 wwn;
+ u64 host_mac;
+
+ /* The MAC is in NO, so flip only the low 48 bits */
+ host_mac = ((u64) mac[0] << 40) |
+ ((u64) mac[1] << 32) |
+ ((u64) mac[2] << 24) |
+ ((u64) mac[3] << 16) |
+ ((u64) mac[4] << 8) |
+ (u64) mac[5];
+
+ WARN_ON(host_mac >= (1ULL << 48));
+ wwn = host_mac | ((u64) scheme << 60);
+ switch (scheme) {
+ case 1:
+ WARN_ON(port != 0);
+ break;
+ case 2:
+ WARN_ON(port >= 0xfff);
+ wwn |= (u64) port << 48;
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ return wwn;
+}
+EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
+/*
+ * fcoe_hostlist_lookup_softc - find the corresponding lport by a given device
+ * @device: this is currently ptr to net_device
+ *
+ * Returns: NULL or the located fcoe_softc
+ */
+static struct fcoe_softc *fcoe_hostlist_lookup_softc(
+ const struct net_device *dev)
+{
+ struct fcoe_softc *fc;
+
+ read_lock(&fcoe_hostlist_lock);
+ list_for_each_entry(fc, &fcoe_hostlist, list) {
+ if (fc->real_dev == dev) {
+ read_unlock(&fcoe_hostlist_lock);
+ return fc;
+ }
+ }
+ read_unlock(&fcoe_hostlist_lock);
+ return NULL;
+}
+
+/*
+ * fcoe_hostlist_lookup - find the corresponding lport by netdev
+ * @netdev: ptr to net_device
+ *
+ * Returns: 0 for success
+ */
+struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
+{
+ struct fcoe_softc *fc;
+
+ fc = fcoe_hostlist_lookup_softc(netdev);
+
+ return (fc) ? fc->lp : NULL;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup);
+
+/*
+ * fcoe_hostlist_add - add a lport to lports list
+ * @lp: ptr to the fc_lport to badded
+ *
+ * Returns: 0 for success
+ */
+int fcoe_hostlist_add(const struct fc_lport *lp)
+{
+ struct fcoe_softc *fc;
+
+ fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp));
+ if (!fc) {
+ fc = fcoe_softc(lp);
+ write_lock_bh(&fcoe_hostlist_lock);
+ list_add_tail(&fc->list, &fcoe_hostlist);
+ write_unlock_bh(&fcoe_hostlist_lock);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_add);
+
+/*
+ * fcoe_hostlist_remove - remove a lport from lports list
+ * @lp: ptr to the fc_lport to badded
+ *
+ * Returns: 0 for success
+ */
+int fcoe_hostlist_remove(const struct fc_lport *lp)
+{
+ struct fcoe_softc *fc;
+
+ fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp));
+ BUG_ON(!fc);
+ write_lock_bh(&fcoe_hostlist_lock);
+ list_del(&fc->list);
+ write_unlock_bh(&fcoe_hostlist_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_remove);
+
+/**
+ * fcoe_libfc_config - sets up libfc related properties for lport
+ * @lp: ptr to the fc_lport
+ * @tt: libfc function template
+ *
+ * Returns : 0 for success
+ **/
+int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
+{
+ /* Set the function pointers set by the LLDD */
+ memcpy(&lp->tt, tt, sizeof(*tt));
+ if (fc_fcp_init(lp))
+ return -ENOMEM;
+ fc_exch_init(lp);
+ fc_elsct_init(lp);
+ fc_lport_init(lp);
+ fc_rport_init(lp);
+ fc_disc_init(lp);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
+/**
+ * fcoe_init - fcoe module loading initialization
+ *
+ * Initialization routine
+ * 1. Will create fc transport software structure
+ * 2. initialize the link list of port information structure
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int __init fcoe_init(void)
+{
+ int cpu;
+ struct fcoe_percpu_s *p;
+
+
+ INIT_LIST_HEAD(&fcoe_hostlist);
+ rwlock_init(&fcoe_hostlist_lock);
+
+#ifdef CONFIG_HOTPLUG_CPU
+ register_cpu_notifier(&fcoe_cpu_notifier);
+#endif /* CONFIG_HOTPLUG_CPU */
+
+ /*
+ * initialize per CPU interrupt thread
+ */
+ for_each_online_cpu(cpu) {
+ p = kzalloc(sizeof(struct fcoe_percpu_s), GFP_KERNEL);
+ if (p) {
+ p->thread = kthread_create(fcoe_percpu_receive_thread,
+ (void *)p,
+ "fcoethread/%d", cpu);
+
+ /*
+ * if there is no error then bind the thread to the cpu
+ * initialize the semaphore and skb queue head
+ */
+ if (likely(!IS_ERR(p->thread))) {
+ p->cpu = cpu;
+ fcoe_percpu[cpu] = p;
+ skb_queue_head_init(&p->fcoe_rx_list);
+ kthread_bind(p->thread, cpu);
+ wake_up_process(p->thread);
+ } else {
+ fcoe_percpu[cpu] = NULL;
+ kfree(p);
+
+ }
+ }
+ }
+
+ /*
+ * setup link change notification
+ */
+ fcoe_dev_setup();
+
+ init_timer(&fcoe_timer);
+ fcoe_timer.data = 0;
+ fcoe_timer.function = fcoe_watchdog;
+ fcoe_timer.expires = (jiffies + (10 * HZ));
+ add_timer(&fcoe_timer);
+
+ /* initiatlize the fcoe transport */
+ fcoe_transport_init();
+
+ fcoe_sw_init();
+
+ return 0;
+}
+module_init(fcoe_init);
+
+/**
+ * fcoe_exit - fcoe module unloading cleanup
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static void __exit fcoe_exit(void)
+{
+ u32 idx;
+ struct fcoe_softc *fc, *tmp;
+ struct fcoe_percpu_s *p;
+ struct sk_buff *skb;
+
+ /*
+ * Stop all call back interfaces
+ */
+#ifdef CONFIG_HOTPLUG_CPU
+ unregister_cpu_notifier(&fcoe_cpu_notifier);
+#endif /* CONFIG_HOTPLUG_CPU */
+ fcoe_dev_cleanup();
+
+ /*
+ * stop timer
+ */
+ del_timer_sync(&fcoe_timer);
+
+ /* releases the assocaited fcoe transport for each lport */
+ list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
+ fcoe_transport_release(fc->real_dev);
+
+ for (idx = 0; idx < NR_CPUS; idx++) {
+ if (fcoe_percpu[idx]) {
+ kthread_stop(fcoe_percpu[idx]->thread);
+ p = fcoe_percpu[idx];
+ spin_lock_bh(&p->fcoe_rx_list.lock);
+ while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+ kfree_skb(skb);
+ spin_unlock_bh(&p->fcoe_rx_list.lock);
+ if (fcoe_percpu[idx]->crc_eof_page)
+ put_page(fcoe_percpu[idx]->crc_eof_page);
+ kfree(fcoe_percpu[idx]);
+ }
+ }
+
+ /* remove sw trasnport */
+ fcoe_sw_exit();
+
+ /* detach the transport */
+ fcoe_transport_exit();
+}
+module_exit(fcoe_exit);
diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c
index 56f4e6bffc21..32eef66114c7 100644
--- a/drivers/scsi/fdomain.c
+++ b/drivers/scsi/fdomain.c
@@ -3,7 +3,7 @@
* Revised: Mon Dec 28 21:59:02 1998 by faith@acm.org
* Author: Rickard E. Faith, faith@cs.unc.edu
* Copyright 1992-1996, 1998 Rickard E. Faith (faith@acm.org)
- * Shared IRQ supported added 7/7/2001 Alan Cox <alan@redhat.com>
+ * Shared IRQ supported added 7/7/2001 Alan Cox <alan@lxorguk.ukuu.org.uk>
* 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
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
index 4d15a62914e9..9c1e6a5b5af0 100644
--- a/drivers/scsi/ibmmca.c
+++ b/drivers/scsi/ibmmca.c
@@ -10,7 +10,7 @@
See the WWW-page: http://www.uni-mainz.de/~langm000/linux.html for latest
updates, info and ADF-files for adapters supported by this driver.
- Alan Cox <alan@redhat.com>
+ Alan Cox <alan@lxorguk.ukuu.org.uk>
Updated for Linux 2.5.45 to use the new error handler, cleaned up the
lock macros and did a few unavoidable locking tweaks, plus one locking
fix in the irq and completion path.
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 7650707a40de..44f202f33101 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -121,6 +121,7 @@ static const struct {
{ IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED, DID_ABORT, 0, 1, "transaction cancelled" },
{ IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED_IMPLICIT, DID_ABORT, 0, 1, "transaction cancelled implicit" },
{ IBMVFC_VIOS_FAILURE, IBMVFC_INSUFFICIENT_RESOURCE, DID_REQUEUE, 1, 1, "insufficient resources" },
+ { IBMVFC_VIOS_FAILURE, IBMVFC_PLOGI_REQUIRED, DID_ERROR, 0, 1, "port login required" },
{ IBMVFC_VIOS_FAILURE, IBMVFC_COMMAND_FAILED, DID_ERROR, 1, 1, "command failed" },
{ IBMVFC_FC_FAILURE, IBMVFC_INVALID_ELS_CMD_CODE, DID_ERROR, 0, 1, "invalid ELS command code" },
@@ -278,13 +279,6 @@ static int ibmvfc_get_err_result(struct ibmvfc_cmd *vfc_cmd)
rsp->data.info.rsp_code))
return DID_ERROR << 16;
- if (!vfc_cmd->status) {
- if (rsp->flags & FCP_RESID_OVER)
- return rsp->scsi_status | (DID_ERROR << 16);
- else
- return rsp->scsi_status | (DID_OK << 16);
- }
-
err = ibmvfc_get_err_index(vfc_cmd->status, vfc_cmd->error);
if (err >= 0)
return rsp->scsi_status | (cmd_status[err].result << 16);
@@ -503,6 +497,7 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
case IBMVFC_HOST_ACTION_INIT:
case IBMVFC_HOST_ACTION_TGT_DEL:
case IBMVFC_HOST_ACTION_QUERY_TGTS:
+ case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
case IBMVFC_HOST_ACTION_TGT_ADD:
case IBMVFC_HOST_ACTION_NONE:
default:
@@ -566,7 +561,7 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
struct ibmvfc_target *tgt;
if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
- if (++vhost->init_retries > IBMVFC_MAX_INIT_RETRIES) {
+ if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
dev_err(vhost->dev,
"Host initialization retries exceeded. Taking adapter offline\n");
ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
@@ -765,6 +760,9 @@ static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
cmnd->scsi_done(cmnd);
}
+ if (evt->eh_comp)
+ complete(evt->eh_comp);
+
ibmvfc_free_event(evt);
}
@@ -847,11 +845,12 @@ static void ibmvfc_reset_host(struct ibmvfc_host *vhost)
static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
{
if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
- if (++vhost->init_retries > IBMVFC_MAX_INIT_RETRIES) {
+ vhost->delay_init = 1;
+ if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
dev_err(vhost->dev,
"Host initialization retries exceeded. Taking adapter offline\n");
ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
- } else if (vhost->init_retries == IBMVFC_MAX_INIT_RETRIES)
+ } else if (vhost->init_retries == IBMVFC_MAX_HOST_INIT_RETRIES)
__ibmvfc_reset_host(vhost);
else
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
@@ -1252,6 +1251,7 @@ static void ibmvfc_init_event(struct ibmvfc_event *evt,
evt->sync_iu = NULL;
evt->crq.format = format;
evt->done = done;
+ evt->eh_comp = NULL;
}
/**
@@ -1381,6 +1381,8 @@ static int ibmvfc_send_event(struct ibmvfc_event *evt,
add_timer(&evt->timer);
}
+ mb();
+
if ((rc = ibmvfc_send_crq(vhost, crq_as_u64[0], crq_as_u64[1]))) {
list_del(&evt->queue);
del_timer(&evt->timer);
@@ -1477,6 +1479,11 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
sense_len = SCSI_SENSE_BUFFERSIZE - rsp_len;
if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len && rsp_len <= 8)
memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
+ if ((vfc_cmd->status & IBMVFC_VIOS_FAILURE) && (vfc_cmd->error == IBMVFC_PLOGI_REQUIRED))
+ ibmvfc_reinit_host(evt->vhost);
+
+ if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
+ cmnd->result = (DID_ERROR << 16);
ibmvfc_log_error(evt);
}
@@ -1489,6 +1496,9 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
cmnd->scsi_done(cmnd);
}
+ if (evt->eh_comp)
+ complete(evt->eh_comp);
+
ibmvfc_free_event(evt);
}
@@ -1627,7 +1637,7 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
struct ibmvfc_host *vhost = shost_priv(sdev->host);
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
struct ibmvfc_cmd *tmf;
- struct ibmvfc_event *evt;
+ struct ibmvfc_event *evt = NULL;
union ibmvfc_iu rsp_iu;
struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
int rsp_rc = -EBUSY;
@@ -1789,7 +1799,8 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
{
struct ibmvfc_host *vhost = shost_priv(sdev->host);
- struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+ struct scsi_target *starget = scsi_target(sdev);
+ struct fc_rport *rport = starget_to_rport(starget);
struct ibmvfc_tmf *tmf;
struct ibmvfc_event *evt, *found_evt;
union ibmvfc_iu rsp;
@@ -1827,7 +1838,7 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
int_to_scsilun(sdev->lun, &tmf->lun);
tmf->flags = (type | IBMVFC_TMF_LUA_VALID);
tmf->cancel_key = (unsigned long)sdev->hostdata;
- tmf->my_cancel_key = (IBMVFC_TMF_CANCEL_KEY | (unsigned long)sdev->hostdata);
+ tmf->my_cancel_key = (unsigned long)starget->hostdata;
evt->sync_iu = &rsp;
init_completion(&evt->comp);
@@ -1859,6 +1870,91 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
}
/**
+ * ibmvfc_match_target - Match function for specified target
+ * @evt: ibmvfc event struct
+ * @device: device to match (starget)
+ *
+ * Returns:
+ * 1 if event matches starget / 0 if event does not match starget
+ **/
+static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device)
+{
+ if (evt->cmnd && scsi_target(evt->cmnd->device) == device)
+ return 1;
+ return 0;
+}
+
+/**
+ * ibmvfc_match_lun - Match function for specified LUN
+ * @evt: ibmvfc event struct
+ * @device: device to match (sdev)
+ *
+ * Returns:
+ * 1 if event matches sdev / 0 if event does not match sdev
+ **/
+static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
+{
+ if (evt->cmnd && evt->cmnd->device == device)
+ return 1;
+ return 0;
+}
+
+/**
+ * ibmvfc_wait_for_ops - Wait for ops to complete
+ * @vhost: ibmvfc host struct
+ * @device: device to match (starget or sdev)
+ * @match: match function
+ *
+ * Returns:
+ * SUCCESS / FAILED
+ **/
+static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
+ int (*match) (struct ibmvfc_event *, void *))
+{
+ struct ibmvfc_event *evt;
+ DECLARE_COMPLETION_ONSTACK(comp);
+ int wait;
+ unsigned long flags;
+ signed long timeout = init_timeout * HZ;
+
+ ENTER;
+ do {
+ wait = 0;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ list_for_each_entry(evt, &vhost->sent, queue) {
+ if (match(evt, device)) {
+ evt->eh_comp = &comp;
+ wait++;
+ }
+ }
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ if (wait) {
+ timeout = wait_for_completion_timeout(&comp, timeout);
+
+ if (!timeout) {
+ wait = 0;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ list_for_each_entry(evt, &vhost->sent, queue) {
+ if (match(evt, device)) {
+ evt->eh_comp = NULL;
+ wait++;
+ }
+ }
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ if (wait)
+ dev_err(vhost->dev, "Timed out waiting for aborted commands\n");
+ LEAVE;
+ return wait ? FAILED : SUCCESS;
+ }
+ }
+ } while (wait);
+
+ LEAVE;
+ return SUCCESS;
+}
+
+/**
* ibmvfc_eh_abort_handler - Abort a command
* @cmd: scsi command to abort
*
@@ -1867,29 +1963,21 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
**/
static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
{
- struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
- struct ibmvfc_event *evt, *pos;
+ struct scsi_device *sdev = cmd->device;
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
int cancel_rc, abort_rc;
- unsigned long flags;
+ int rc = FAILED;
ENTER;
ibmvfc_wait_while_resetting(vhost);
- cancel_rc = ibmvfc_cancel_all(cmd->device, IBMVFC_TMF_ABORT_TASK_SET);
- abort_rc = ibmvfc_abort_task_set(cmd->device);
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
+ abort_rc = ibmvfc_abort_task_set(sdev);
- if (!cancel_rc && !abort_rc) {
- spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
- if (evt->cmnd && evt->cmnd->device == cmd->device)
- ibmvfc_fail_request(evt, DID_ABORT);
- }
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- LEAVE;
- return SUCCESS;
- }
+ if (!cancel_rc && !abort_rc)
+ rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
LEAVE;
- return FAILED;
+ return rc;
}
/**
@@ -1901,29 +1989,21 @@ static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
**/
static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
{
- struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
- struct ibmvfc_event *evt, *pos;
+ struct scsi_device *sdev = cmd->device;
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
int cancel_rc, reset_rc;
- unsigned long flags;
+ int rc = FAILED;
ENTER;
ibmvfc_wait_while_resetting(vhost);
- cancel_rc = ibmvfc_cancel_all(cmd->device, IBMVFC_TMF_LUN_RESET);
- reset_rc = ibmvfc_reset_device(cmd->device, IBMVFC_LUN_RESET, "LUN");
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET);
+ reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN");
- if (!cancel_rc && !reset_rc) {
- spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
- if (evt->cmnd && evt->cmnd->device == cmd->device)
- ibmvfc_fail_request(evt, DID_ABORT);
- }
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- LEAVE;
- return SUCCESS;
- }
+ if (!cancel_rc && !reset_rc)
+ rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
LEAVE;
- return FAILED;
+ return rc;
}
/**
@@ -1959,31 +2039,23 @@ static void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data)
**/
static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
{
- struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
- struct scsi_target *starget = scsi_target(cmd->device);
- struct ibmvfc_event *evt, *pos;
+ struct scsi_device *sdev = cmd->device;
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
+ struct scsi_target *starget = scsi_target(sdev);
int reset_rc;
+ int rc = FAILED;
unsigned long cancel_rc = 0;
- unsigned long flags;
ENTER;
ibmvfc_wait_while_resetting(vhost);
starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
- reset_rc = ibmvfc_reset_device(cmd->device, IBMVFC_TARGET_RESET, "target");
+ reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
- if (!cancel_rc && !reset_rc) {
- spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
- if (evt->cmnd && scsi_target(evt->cmnd->device) == starget)
- ibmvfc_fail_request(evt, DID_ABORT);
- }
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- LEAVE;
- return SUCCESS;
- }
+ if (!cancel_rc && !reset_rc)
+ rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
LEAVE;
- return FAILED;
+ return rc;
}
/**
@@ -2013,23 +2085,18 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
struct scsi_target *starget = to_scsi_target(&rport->dev);
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct ibmvfc_host *vhost = shost_priv(shost);
- struct ibmvfc_event *evt, *pos;
unsigned long cancel_rc = 0;
unsigned long abort_rc = 0;
- unsigned long flags;
+ int rc = FAILED;
ENTER;
starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all);
- if (!cancel_rc && !abort_rc) {
- spin_lock_irqsave(shost->host_lock, flags);
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
- if (evt->cmnd && scsi_target(evt->cmnd->device) == starget)
- ibmvfc_fail_request(evt, DID_ABORT);
- }
- spin_unlock_irqrestore(shost->host_lock, flags);
- } else
+ if (!cancel_rc && !abort_rc)
+ rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
+
+ if (rc == FAILED)
ibmvfc_issue_fc_host_lip(shost);
LEAVE;
}
@@ -2089,15 +2156,17 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
case IBMVFC_AE_LINK_UP:
case IBMVFC_AE_RESUME:
vhost->events_to_log |= IBMVFC_AE_LINKUP;
- ibmvfc_init_host(vhost, 1);
+ vhost->delay_init = 1;
+ __ibmvfc_reset_host(vhost);
break;
case IBMVFC_AE_SCN_FABRIC:
+ case IBMVFC_AE_SCN_DOMAIN:
vhost->events_to_log |= IBMVFC_AE_RSCN;
- ibmvfc_init_host(vhost, 1);
+ vhost->delay_init = 1;
+ __ibmvfc_reset_host(vhost);
break;
case IBMVFC_AE_SCN_NPORT:
case IBMVFC_AE_SCN_GROUP:
- case IBMVFC_AE_SCN_DOMAIN:
vhost->events_to_log |= IBMVFC_AE_RSCN;
case IBMVFC_AE_ELS_LOGO:
case IBMVFC_AE_ELS_PRLO:
@@ -2263,6 +2332,28 @@ static int ibmvfc_slave_alloc(struct scsi_device *sdev)
}
/**
+ * ibmvfc_target_alloc - Setup the target's task set value
+ * @starget: struct scsi_target
+ *
+ * Set the target's task set value so that error handling works as
+ * expected.
+ *
+ * Returns:
+ * 0 on success / -ENXIO if device does not exist
+ **/
+static int ibmvfc_target_alloc(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ibmvfc_host *vhost = shost_priv(shost);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ starget->hostdata = (void *)(unsigned long)vhost->task_set++;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ return 0;
+}
+
+/**
* ibmvfc_slave_configure - Configure the device
* @sdev: struct scsi_device device to configure
*
@@ -2541,6 +2632,7 @@ static struct scsi_host_template driver_template = {
.eh_host_reset_handler = ibmvfc_eh_host_reset_handler,
.slave_alloc = ibmvfc_slave_alloc,
.slave_configure = ibmvfc_slave_configure,
+ .target_alloc = ibmvfc_target_alloc,
.scan_finished = ibmvfc_scan_finished,
.change_queue_depth = ibmvfc_change_queue_depth,
.change_queue_type = ibmvfc_change_queue_type,
@@ -2637,7 +2729,7 @@ static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance)
} else if ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
vio_disable_interrupts(vdev);
ibmvfc_handle_async(async, vhost);
- crq->valid = 0;
+ async->valid = 0;
} else
done = 1;
}
@@ -2669,7 +2761,7 @@ static void ibmvfc_init_tgt(struct ibmvfc_target *tgt,
static void ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
void (*job_step) (struct ibmvfc_target *))
{
- if (++tgt->init_retries > IBMVFC_MAX_INIT_RETRIES) {
+ if (++tgt->init_retries > IBMVFC_MAX_TGT_INIT_RETRIES) {
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
wake_up(&tgt->vhost->work_wait_q);
} else
@@ -2708,6 +2800,8 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
rsp->status, rsp->error, status);
if (ibmvfc_retry_cmd(rsp->status, rsp->error))
ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
+ else
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
break;
};
@@ -2802,6 +2896,8 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
if (ibmvfc_retry_cmd(rsp->status, rsp->error))
ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
+ else
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
break;
};
@@ -3093,6 +3189,8 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
else if (ibmvfc_retry_cmd(rsp->status, rsp->error))
ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
+ else
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
break;
};
@@ -3423,6 +3521,7 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
case IBMVFC_HOST_ACTION_ALLOC_TGTS:
case IBMVFC_HOST_ACTION_TGT_ADD:
case IBMVFC_HOST_ACTION_TGT_DEL:
+ case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
case IBMVFC_HOST_ACTION_QUERY:
default:
break;
@@ -3519,7 +3618,13 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
break;
case IBMVFC_HOST_ACTION_INIT:
BUG_ON(vhost->state != IBMVFC_INITIALIZING);
- vhost->job_step(vhost);
+ if (vhost->delay_init) {
+ vhost->delay_init = 0;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ssleep(15);
+ return;
+ } else
+ vhost->job_step(vhost);
break;
case IBMVFC_HOST_ACTION_QUERY:
list_for_each_entry(tgt, &vhost->targets, queue)
@@ -3538,6 +3643,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
break;
case IBMVFC_HOST_ACTION_TGT_DEL:
+ case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
list_for_each_entry(tgt, &vhost->targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
tgt_dbg(tgt, "Deleting rport\n");
@@ -3553,8 +3659,17 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
}
if (vhost->state == IBMVFC_INITIALIZING) {
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
- vhost->job_step = ibmvfc_discover_targets;
+ if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) {
+ ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_ADD);
+ vhost->init_retries = 0;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ scsi_unblock_requests(vhost->host);
+ return;
+ } else {
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
+ vhost->job_step = ibmvfc_discover_targets;
+ }
} else {
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -3577,14 +3692,8 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
}
}
- if (!ibmvfc_dev_init_to_do(vhost)) {
- ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_ADD);
- vhost->init_retries = 0;
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- scsi_unblock_requests(vhost->host);
- return;
- }
+ if (!ibmvfc_dev_init_to_do(vhost))
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL_FAILED);
break;
case IBMVFC_HOST_ACTION_TGT_ADD:
list_for_each_entry(tgt, &vhost->targets, queue) {
@@ -3592,16 +3701,6 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
spin_unlock_irqrestore(vhost->host->host_lock, flags);
ibmvfc_tgt_add_rport(tgt);
return;
- } else if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
- tgt_dbg(tgt, "Deleting rport\n");
- rport = tgt->rport;
- tgt->rport = NULL;
- list_del(&tgt->queue);
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- if (rport)
- fc_remote_port_delete(rport);
- kref_put(&tgt->kref, ibmvfc_release_tgt);
- return;
}
}
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index fb3177ab6691..babdf3db59df 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -29,11 +29,11 @@
#include "viosrp.h"
#define IBMVFC_NAME "ibmvfc"
-#define IBMVFC_DRIVER_VERSION "1.0.2"
-#define IBMVFC_DRIVER_DATE "(August 14, 2008)"
+#define IBMVFC_DRIVER_VERSION "1.0.4"
+#define IBMVFC_DRIVER_DATE "(November 14, 2008)"
#define IBMVFC_DEFAULT_TIMEOUT 15
-#define IBMVFC_INIT_TIMEOUT 30
+#define IBMVFC_INIT_TIMEOUT 120
#define IBMVFC_MAX_REQUESTS_DEFAULT 100
#define IBMVFC_DEBUG 0
@@ -43,7 +43,8 @@
#define IBMVFC_MAX_DISC_THREADS 4
#define IBMVFC_TGT_MEMPOOL_SZ 64
#define IBMVFC_MAX_CMDS_PER_LUN 64
-#define IBMVFC_MAX_INIT_RETRIES 3
+#define IBMVFC_MAX_HOST_INIT_RETRIES 6
+#define IBMVFC_MAX_TGT_INIT_RETRIES 3
#define IBMVFC_DEV_LOSS_TMO (5 * 60)
#define IBMVFC_DEFAULT_LOG_LEVEL 2
#define IBMVFC_MAX_CDB_LEN 16
@@ -109,6 +110,7 @@ enum ibmvfc_vios_errors {
IBMVFC_TRANS_CANCELLED = 0x0006,
IBMVFC_TRANS_CANCELLED_IMPLICIT = 0x0007,
IBMVFC_INSUFFICIENT_RESOURCE = 0x0008,
+ IBMVFC_PLOGI_REQUIRED = 0x0010,
IBMVFC_COMMAND_FAILED = 0x8000,
};
@@ -337,7 +339,6 @@ struct ibmvfc_tmf {
#define IBMVFC_TMF_LUA_VALID 0x40
u32 cancel_key;
u32 my_cancel_key;
-#define IBMVFC_TMF_CANCEL_KEY 0x80000000
u32 pad;
u64 reserved[2];
}__attribute__((packed, aligned (8)));
@@ -524,10 +525,10 @@ enum ibmvfc_async_event {
};
struct ibmvfc_crq {
- u8 valid;
- u8 format;
+ volatile u8 valid;
+ volatile u8 format;
u8 reserved[6];
- u64 ioba;
+ volatile u64 ioba;
}__attribute__((packed, aligned (8)));
struct ibmvfc_crq_queue {
@@ -537,13 +538,13 @@ struct ibmvfc_crq_queue {
};
struct ibmvfc_async_crq {
- u8 valid;
+ volatile u8 valid;
u8 pad[3];
u32 pad2;
- u64 event;
- u64 scsi_id;
- u64 wwpn;
- u64 node_name;
+ volatile u64 event;
+ volatile u64 scsi_id;
+ volatile u64 wwpn;
+ volatile u64 node_name;
u64 reserved;
}__attribute__((packed, aligned (8)));
@@ -606,6 +607,7 @@ struct ibmvfc_event {
struct srp_direct_buf *ext_list;
dma_addr_t ext_list_token;
struct completion comp;
+ struct completion *eh_comp;
struct timer_list timer;
};
@@ -626,6 +628,7 @@ enum ibmvfc_host_action {
IBMVFC_HOST_ACTION_TGT_DEL,
IBMVFC_HOST_ACTION_ALLOC_TGTS,
IBMVFC_HOST_ACTION_TGT_INIT,
+ IBMVFC_HOST_ACTION_TGT_DEL_FAILED,
IBMVFC_HOST_ACTION_TGT_ADD,
};
@@ -671,6 +674,7 @@ struct ibmvfc_host {
int discovery_threads;
int client_migrated;
int reinit;
+ int delay_init;
int events_to_log;
#define IBMVFC_AE_LINKUP 0x0001
#define IBMVFC_AE_LINKDOWN 0x0002
@@ -700,7 +704,7 @@ struct ibmvfc_host {
#define ibmvfc_log(vhost, level, ...) \
do { \
- if (level >= (vhost)->log_level) \
+ if ((vhost)->log_level >= level) \
dev_err((vhost)->dev, ##__VA_ARGS__); \
} while (0)
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index 6cad1758243a..868d35ea01bb 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -107,7 +107,7 @@ module_param_named(max_channel, max_channel, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(max_channel, "Largest channel value");
module_param_named(init_timeout, init_timeout, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds");
-module_param_named(max_requests, max_requests, int, S_IRUGO | S_IWUSR);
+module_param_named(max_requests, max_requests, int, S_IRUGO);
MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter");
/* ------------------------------------------------------------
@@ -1657,7 +1657,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
vdev->dev.driver_data = NULL;
- driver_template.can_queue = max_requests;
+ driver_template.can_queue = max_requests - 2;
host = scsi_host_alloc(&driver_template, sizeof(*hostdata));
if (!host) {
dev_err(&vdev->dev, "couldn't allocate host data\n");
diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
index 2370fd82ebfe..c24140aff8e7 100644
--- a/drivers/scsi/ide-scsi.c
+++ b/drivers/scsi/ide-scsi.c
@@ -578,6 +578,8 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd)
{
idescsi_scsi_t *scsi = scsihost_to_idescsi(cmd->device->host);
ide_drive_t *drive = scsi->drive;
+ ide_hwif_t *hwif;
+ ide_hwgroup_t *hwgroup;
int busy;
int ret = FAILED;
@@ -594,13 +596,16 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd)
goto no_drive;
}
- /* First give it some more time, how much is "right" is hard to say :-( */
+ hwif = drive->hwif;
+ hwgroup = hwif->hwgroup;
- busy = ide_wait_not_busy(HWIF(drive), 100); /* FIXME - uses mdelay which causes latency? */
+ /* First give it some more time, how much is "right" is hard to say :-(
+ FIXME - uses mdelay which causes latency? */
+ busy = ide_wait_not_busy(hwif, 100);
if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
printk (KERN_WARNING "ide-scsi: drive did%s become ready\n", busy?" not":"");
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
/* If there is no pc running we're done (our interrupt took care of it) */
pc = drive->pc;
@@ -629,7 +634,7 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd)
}
ide_unlock:
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
no_drive:
if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
printk (KERN_WARNING "ide-scsi: abort returns %s\n", ret == SUCCESS?"success":"failed");
@@ -642,6 +647,7 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd)
struct request *req;
idescsi_scsi_t *scsi = scsihost_to_idescsi(cmd->device->host);
ide_drive_t *drive = scsi->drive;
+ ide_hwgroup_t *hwgroup;
int ready = 0;
int ret = SUCCESS;
@@ -658,14 +664,18 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd)
return FAILED;
}
+ hwgroup = drive->hwif->hwgroup;
+
spin_lock_irq(cmd->device->host->host_lock);
- spin_lock(&ide_lock);
+ spin_lock(&hwgroup->lock);
pc = drive->pc;
+ if (pc)
+ req = pc->rq;
- if (pc == NULL || (req = pc->rq) != HWGROUP(drive)->rq || !HWGROUP(drive)->handler) {
+ if (pc == NULL || req != hwgroup->rq || hwgroup->handler == NULL) {
printk (KERN_WARNING "ide-scsi: No active request in idescsi_eh_reset\n");
- spin_unlock(&ide_lock);
+ spin_unlock(&hwgroup->lock);
spin_unlock_irq(cmd->device->host->host_lock);
return FAILED;
}
@@ -685,10 +695,10 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd)
BUG();
}
- HWGROUP(drive)->rq = NULL;
- HWGROUP(drive)->handler = NULL;
- HWGROUP(drive)->busy = 1; /* will set this to zero when ide reset finished */
- spin_unlock(&ide_lock);
+ hwgroup->rq = NULL;
+ hwgroup->handler = NULL;
+ hwgroup->busy = 1; /* will set this to zero when ide reset finished */
+ spin_unlock(&hwgroup->lock);
ide_do_reset(drive);
diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c
index 8053b1e86ccb..52bdc6df6b92 100644
--- a/drivers/scsi/in2000.c
+++ b/drivers/scsi/in2000.c
@@ -107,7 +107,7 @@
* this thing into as good a shape as possible, and I'm positive
* there are lots of lurking bugs and "Stupid Places".
*
- * Updated for Linux 2.5 by Alan Cox <alan@redhat.com>
+ * Updated for Linux 2.5 by Alan Cox <alan@lxorguk.ukuu.org.uk>
* - Using new_eh handler
* - Hopefully got all the locking right again
* See "FIXME" notes for items that could do with more work
diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c
index e3f739776bad..5529518ff2fa 100644
--- a/drivers/scsi/initio.c
+++ b/drivers/scsi/initio.c
@@ -4,7 +4,7 @@
* Copyright (c) 1994-1998 Initio Corporation
* Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl>
* Copyright (c) 2004 Christoph Hellwig <hch@lst.de>
- * Copyright (c) 2007 Red Hat <alan@redhat.com>
+ * Copyright (c) 2007 Red Hat
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/drivers/scsi/initio.h b/drivers/scsi/initio.h
index cb48efa81fe2..e58af9e95506 100644
--- a/drivers/scsi/initio.h
+++ b/drivers/scsi/initio.h
@@ -4,7 +4,7 @@
* Copyright (c) 1994-1998 Initio Corporation
* All rights reserved.
*
- * Cleanups (c) Copyright 2007 Red Hat <alan@redhat.com>
+ * Cleanups (c) Copyright 2007 Red Hat <alan@lxorguk.ukuu.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index ded854a6dd35..0edfb1fa63ce 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -5389,9 +5389,9 @@ static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd)
list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
wake_up_all(&ioa_cfg->reset_wait_q);
- spin_unlock_irq(ioa_cfg->host->host_lock);
+ spin_unlock(ioa_cfg->host->host_lock);
scsi_unblock_requests(ioa_cfg->host);
- spin_lock_irq(ioa_cfg->host->host_lock);
+ spin_lock(ioa_cfg->host->host_lock);
if (!ioa_cfg->allow_cmds)
scsi_block_requests(ioa_cfg->host);
@@ -7473,7 +7473,7 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
goto out_scsi_host_put;
}
- ipr_regs = ioremap(ipr_regs_pci, pci_resource_len(pdev, 0));
+ ipr_regs = pci_ioremap_bar(pdev, 0);
if (!ipr_regs) {
dev_err(&pdev->dev,
diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h
index 4871dd1f2582..59459141b437 100644
--- a/drivers/scsi/ipr.h
+++ b/drivers/scsi/ipr.h
@@ -19,7 +19,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- * Alan Cox <alan@redhat.com> - Removed several careless u32/dma_addr_t errors
+ * Alan Cox <alan@lxorguk.ukuu.org.uk> - Removed several careless u32/dma_addr_t errors
* that broke 64bit platforms.
*/
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index e11bce6ab63c..23808dfe22ba 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -27,7 +27,6 @@
*/
#include <linux/types.h>
-#include <linux/list.h>
#include <linux/inet.h>
#include <linux/file.h>
#include <linux/blkdev.h>
@@ -44,12 +43,12 @@
#include "iscsi_tcp.h"
-MODULE_AUTHOR("Dmitry Yusupov <dmitry_yus@yahoo.com>, "
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, "
+ "Dmitry Yusupov <dmitry_yus@yahoo.com>, "
"Alex Aizman <itn780@yahoo.com>");
MODULE_DESCRIPTION("iSCSI/TCP data-path");
MODULE_LICENSE("GPL");
#undef DEBUG_TCP
-#define DEBUG_ASSERT
#ifdef DEBUG_TCP
#define debug_tcp(fmt...) printk(KERN_INFO "tcp: " fmt)
@@ -57,934 +56,41 @@ MODULE_LICENSE("GPL");
#define debug_tcp(fmt...)
#endif
-#ifndef DEBUG_ASSERT
-#ifdef BUG_ON
-#undef BUG_ON
-#endif
-#define BUG_ON(expr)
-#endif
-
-static struct scsi_transport_template *iscsi_tcp_scsi_transport;
-static struct scsi_host_template iscsi_sht;
-static struct iscsi_transport iscsi_tcp_transport;
+static struct scsi_transport_template *iscsi_sw_tcp_scsi_transport;
+static struct scsi_host_template iscsi_sw_tcp_sht;
+static struct iscsi_transport iscsi_sw_tcp_transport;
static unsigned int iscsi_max_lun = 512;
module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
-static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment);
-
-/*
- * Scatterlist handling: inside the iscsi_segment, we
- * remember an index into the scatterlist, and set data/size
- * to the current scatterlist entry. For highmem pages, we
- * kmap as needed.
- *
- * Note that the page is unmapped when we return from
- * TCP's data_ready handler, so we may end up mapping and
- * unmapping the same page repeatedly. The whole reason
- * for this is that we shouldn't keep the page mapped
- * outside the softirq.
- */
-
-/**
- * iscsi_tcp_segment_init_sg - init indicated scatterlist entry
- * @segment: the buffer object
- * @sg: scatterlist
- * @offset: byte offset into that sg entry
- *
- * This function sets up the segment so that subsequent
- * data is copied to the indicated sg entry, at the given
- * offset.
- */
-static inline void
-iscsi_tcp_segment_init_sg(struct iscsi_segment *segment,
- struct scatterlist *sg, unsigned int offset)
-{
- segment->sg = sg;
- segment->sg_offset = offset;
- segment->size = min(sg->length - offset,
- segment->total_size - segment->total_copied);
- segment->data = NULL;
-}
-
-/**
- * iscsi_tcp_segment_map - map the current S/G page
- * @segment: iscsi_segment
- * @recv: 1 if called from recv path
- *
- * We only need to possibly kmap data if scatter lists are being used,
- * because the iscsi passthrough and internal IO paths will never use high
- * mem pages.
- */
-static inline void
-iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv)
-{
- struct scatterlist *sg;
-
- if (segment->data != NULL || !segment->sg)
- return;
-
- sg = segment->sg;
- BUG_ON(segment->sg_mapped);
- BUG_ON(sg->length == 0);
-
- /*
- * If the page count is greater than one it is ok to send
- * to the network layer's zero copy send path. If not we
- * have to go the slow sendmsg path. We always map for the
- * recv path.
- */
- if (page_count(sg_page(sg)) >= 1 && !recv)
- return;
-
- debug_tcp("iscsi_tcp_segment_map %s %p\n", recv ? "recv" : "xmit",
- segment);
- segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0);
- segment->data = segment->sg_mapped + sg->offset + segment->sg_offset;
-}
-
-static inline void
-iscsi_tcp_segment_unmap(struct iscsi_segment *segment)
-{
- debug_tcp("iscsi_tcp_segment_unmap %p\n", segment);
-
- if (segment->sg_mapped) {
- debug_tcp("iscsi_tcp_segment_unmap valid\n");
- kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0);
- segment->sg_mapped = NULL;
- segment->data = NULL;
- }
-}
-
-/*
- * Splice the digest buffer into the buffer
- */
-static inline void
-iscsi_tcp_segment_splice_digest(struct iscsi_segment *segment, void *digest)
-{
- segment->data = digest;
- segment->digest_len = ISCSI_DIGEST_SIZE;
- segment->total_size += ISCSI_DIGEST_SIZE;
- segment->size = ISCSI_DIGEST_SIZE;
- segment->copied = 0;
- segment->sg = NULL;
- segment->hash = NULL;
-}
-
-/**
- * iscsi_tcp_segment_done - check whether the segment is complete
- * @segment: iscsi segment to check
- * @recv: set to one of this is called from the recv path
- * @copied: number of bytes copied
- *
- * Check if we're done receiving this segment. If the receive
- * buffer is full but we expect more data, move on to the
- * next entry in the scatterlist.
- *
- * If the amount of data we received isn't a multiple of 4,
- * we will transparently receive the pad bytes, too.
- *
- * This function must be re-entrant.
- */
-static inline int
-iscsi_tcp_segment_done(struct iscsi_segment *segment, int recv, unsigned copied)
-{
- static unsigned char padbuf[ISCSI_PAD_LEN];
- struct scatterlist sg;
- unsigned int pad;
-
- debug_tcp("copied %u %u size %u %s\n", segment->copied, copied,
- segment->size, recv ? "recv" : "xmit");
- if (segment->hash && copied) {
- /*
- * If a segment is kmapd we must unmap it before sending
- * to the crypto layer since that will try to kmap it again.
- */
- iscsi_tcp_segment_unmap(segment);
-
- if (!segment->data) {
- sg_init_table(&sg, 1);
- sg_set_page(&sg, sg_page(segment->sg), copied,
- segment->copied + segment->sg_offset +
- segment->sg->offset);
- } else
- sg_init_one(&sg, segment->data + segment->copied,
- copied);
- crypto_hash_update(segment->hash, &sg, copied);
- }
-
- segment->copied += copied;
- if (segment->copied < segment->size) {
- iscsi_tcp_segment_map(segment, recv);
- return 0;
- }
-
- segment->total_copied += segment->copied;
- segment->copied = 0;
- segment->size = 0;
-
- /* Unmap the current scatterlist page, if there is one. */
- iscsi_tcp_segment_unmap(segment);
-
- /* Do we have more scatterlist entries? */
- debug_tcp("total copied %u total size %u\n", segment->total_copied,
- segment->total_size);
- if (segment->total_copied < segment->total_size) {
- /* Proceed to the next entry in the scatterlist. */
- iscsi_tcp_segment_init_sg(segment, sg_next(segment->sg),
- 0);
- iscsi_tcp_segment_map(segment, recv);
- BUG_ON(segment->size == 0);
- return 0;
- }
-
- /* Do we need to handle padding? */
- pad = iscsi_padding(segment->total_copied);
- if (pad != 0) {
- debug_tcp("consume %d pad bytes\n", pad);
- segment->total_size += pad;
- segment->size = pad;
- segment->data = padbuf;
- return 0;
- }
-
- /*
- * Set us up for transferring the data digest. hdr digest
- * is completely handled in hdr done function.
- */
- if (segment->hash) {
- crypto_hash_final(segment->hash, segment->digest);
- iscsi_tcp_segment_splice_digest(segment,
- recv ? segment->recv_digest : segment->digest);
- return 0;
- }
-
- return 1;
-}
-
-/**
- * iscsi_tcp_xmit_segment - transmit segment
- * @tcp_conn: the iSCSI TCP connection
- * @segment: the buffer to transmnit
- *
- * This function transmits as much of the buffer as
- * the network layer will accept, and returns the number of
- * bytes transmitted.
- *
- * If CRC hashing is enabled, the function will compute the
- * hash as it goes. When the entire segment has been transmitted,
- * it will retrieve the hash value and send it as well.
- */
-static int
-iscsi_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment)
-{
- struct socket *sk = tcp_conn->sock;
- unsigned int copied = 0;
- int r = 0;
-
- while (!iscsi_tcp_segment_done(segment, 0, r)) {
- struct scatterlist *sg;
- unsigned int offset, copy;
- int flags = 0;
-
- r = 0;
- offset = segment->copied;
- copy = segment->size - offset;
-
- if (segment->total_copied + segment->size < segment->total_size)
- flags |= MSG_MORE;
-
- /* Use sendpage if we can; else fall back to sendmsg */
- if (!segment->data) {
- sg = segment->sg;
- offset += segment->sg_offset + sg->offset;
- r = tcp_conn->sendpage(sk, sg_page(sg), offset, copy,
- flags);
- } else {
- struct msghdr msg = { .msg_flags = flags };
- struct kvec iov = {
- .iov_base = segment->data + offset,
- .iov_len = copy
- };
-
- r = kernel_sendmsg(sk, &msg, &iov, 1, copy);
- }
-
- if (r < 0) {
- iscsi_tcp_segment_unmap(segment);
- if (copied || r == -EAGAIN)
- break;
- return r;
- }
- copied += r;
- }
- return copied;
-}
-
-/**
- * iscsi_tcp_segment_recv - copy data to segment
- * @tcp_conn: the iSCSI TCP connection
- * @segment: the buffer to copy to
- * @ptr: data pointer
- * @len: amount of data available
- *
- * This function copies up to @len bytes to the
- * given buffer, and returns the number of bytes
- * consumed, which can actually be less than @len.
- *
- * If hash digest is enabled, the function will update the
- * hash while copying.
- * Combining these two operations doesn't buy us a lot (yet),
- * but in the future we could implement combined copy+crc,
- * just way we do for network layer checksums.
- */
-static int
-iscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment, const void *ptr,
- unsigned int len)
-{
- unsigned int copy = 0, copied = 0;
-
- while (!iscsi_tcp_segment_done(segment, 1, copy)) {
- if (copied == len) {
- debug_tcp("iscsi_tcp_segment_recv copied %d bytes\n",
- len);
- break;
- }
-
- copy = min(len - copied, segment->size - segment->copied);
- debug_tcp("iscsi_tcp_segment_recv copying %d\n", copy);
- memcpy(segment->data + segment->copied, ptr + copied, copy);
- copied += copy;
- }
- return copied;
-}
-
-static inline void
-iscsi_tcp_dgst_header(struct hash_desc *hash, const void *hdr, size_t hdrlen,
- unsigned char digest[ISCSI_DIGEST_SIZE])
-{
- struct scatterlist sg;
-
- sg_init_one(&sg, hdr, hdrlen);
- crypto_hash_digest(hash, &sg, hdrlen, digest);
-}
-
-static inline int
-iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment)
-{
- if (!segment->digest_len)
- return 1;
-
- if (memcmp(segment->recv_digest, segment->digest,
- segment->digest_len)) {
- debug_scsi("digest mismatch\n");
- return 0;
- }
-
- return 1;
-}
-
-/*
- * Helper function to set up segment buffer
- */
-static inline void
-__iscsi_segment_init(struct iscsi_segment *segment, size_t size,
- iscsi_segment_done_fn_t *done, struct hash_desc *hash)
-{
- memset(segment, 0, sizeof(*segment));
- segment->total_size = size;
- segment->done = done;
-
- if (hash) {
- segment->hash = hash;
- crypto_hash_init(hash);
- }
-}
-
-static inline void
-iscsi_segment_init_linear(struct iscsi_segment *segment, void *data,
- size_t size, iscsi_segment_done_fn_t *done,
- struct hash_desc *hash)
-{
- __iscsi_segment_init(segment, size, done, hash);
- segment->data = data;
- segment->size = size;
-}
-
-static inline int
-iscsi_segment_seek_sg(struct iscsi_segment *segment,
- struct scatterlist *sg_list, unsigned int sg_count,
- unsigned int offset, size_t size,
- iscsi_segment_done_fn_t *done, struct hash_desc *hash)
-{
- struct scatterlist *sg;
- unsigned int i;
-
- debug_scsi("iscsi_segment_seek_sg offset %u size %llu\n",
- offset, size);
- __iscsi_segment_init(segment, size, done, hash);
- for_each_sg(sg_list, sg, sg_count, i) {
- debug_scsi("sg %d, len %u offset %u\n", i, sg->length,
- sg->offset);
- if (offset < sg->length) {
- iscsi_tcp_segment_init_sg(segment, sg, offset);
- return 0;
- }
- offset -= sg->length;
- }
-
- return ISCSI_ERR_DATA_OFFSET;
-}
-
-/**
- * iscsi_tcp_hdr_recv_prep - prep segment for hdr reception
- * @tcp_conn: iscsi connection to prep for
- *
- * This function always passes NULL for the hash argument, because when this
- * function is called we do not yet know the final size of the header and want
- * to delay the digest processing until we know that.
- */
-static void
-iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn)
-{
- debug_tcp("iscsi_tcp_hdr_recv_prep(%p%s)\n", tcp_conn,
- tcp_conn->iscsi_conn->hdrdgst_en ? ", digest enabled" : "");
- iscsi_segment_init_linear(&tcp_conn->in.segment,
- tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr),
- iscsi_tcp_hdr_recv_done, NULL);
-}
-
-/*
- * Handle incoming reply to any other type of command
- */
-static int
-iscsi_tcp_data_recv_done(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment)
-{
- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
- int rc = 0;
-
- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
- return ISCSI_ERR_DATA_DGST;
-
- rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr,
- conn->data, tcp_conn->in.datalen);
- if (rc)
- return rc;
-
- iscsi_tcp_hdr_recv_prep(tcp_conn);
- return 0;
-}
-
-static void
-iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
-{
- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
- struct hash_desc *rx_hash = NULL;
-
- if (conn->datadgst_en)
- rx_hash = &tcp_conn->rx_hash;
-
- iscsi_segment_init_linear(&tcp_conn->in.segment,
- conn->data, tcp_conn->in.datalen,
- iscsi_tcp_data_recv_done, rx_hash);
-}
-
-/*
- * must be called with session lock
- */
-static void
-iscsi_tcp_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
-{
- struct iscsi_tcp_task *tcp_task = task->dd_data;
- struct iscsi_r2t_info *r2t;
-
- /* nothing to do for mgmt tasks */
- if (!task->sc)
- return;
-
- /* flush task's r2t queues */
- while (__kfifo_get(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) {
- __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
- debug_scsi("iscsi_tcp_cleanup_task pending r2t dropped\n");
- }
-
- r2t = tcp_task->r2t;
- if (r2t != NULL) {
- __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
- tcp_task->r2t = NULL;
- }
-}
-
-/**
- * iscsi_data_in - SCSI Data-In Response processing
- * @conn: iscsi connection
- * @task: scsi command task
- **/
-static int
-iscsi_data_in(struct iscsi_conn *conn, struct iscsi_task *task)
-{
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_tcp_task *tcp_task = task->dd_data;
- struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
- int datasn = be32_to_cpu(rhdr->datasn);
- unsigned total_in_length = scsi_in(task->sc)->length;
-
- iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr);
- if (tcp_conn->in.datalen == 0)
- return 0;
-
- if (tcp_task->exp_datasn != datasn) {
- debug_tcp("%s: task->exp_datasn(%d) != rhdr->datasn(%d)\n",
- __func__, tcp_task->exp_datasn, datasn);
- return ISCSI_ERR_DATASN;
- }
-
- tcp_task->exp_datasn++;
-
- tcp_task->data_offset = be32_to_cpu(rhdr->offset);
- if (tcp_task->data_offset + tcp_conn->in.datalen > total_in_length) {
- debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n",
- __func__, tcp_task->data_offset,
- tcp_conn->in.datalen, total_in_length);
- return ISCSI_ERR_DATA_OFFSET;
- }
-
- conn->datain_pdus_cnt++;
- return 0;
-}
-
-/**
- * iscsi_solicit_data_init - initialize first Data-Out
- * @conn: iscsi connection
- * @task: scsi command task
- * @r2t: R2T info
- *
- * Notes:
- * Initialize first Data-Out within this R2T sequence and finds
- * proper data_offset within this SCSI command.
- *
- * This function is called with connection lock taken.
- **/
-static void
-iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_task *task,
- struct iscsi_r2t_info *r2t)
-{
- struct iscsi_data *hdr;
-
- hdr = &r2t->dtask.hdr;
- memset(hdr, 0, sizeof(struct iscsi_data));
- hdr->ttt = r2t->ttt;
- hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
- r2t->solicit_datasn++;
- hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
- memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
- hdr->itt = task->hdr->itt;
- hdr->exp_statsn = r2t->exp_statsn;
- hdr->offset = cpu_to_be32(r2t->data_offset);
- if (r2t->data_length > conn->max_xmit_dlength) {
- hton24(hdr->dlength, conn->max_xmit_dlength);
- r2t->data_count = conn->max_xmit_dlength;
- hdr->flags = 0;
- } else {
- hton24(hdr->dlength, r2t->data_length);
- r2t->data_count = r2t->data_length;
- hdr->flags = ISCSI_FLAG_CMD_FINAL;
- }
- conn->dataout_pdus_cnt++;
-
- r2t->sent = 0;
-}
-
-/**
- * iscsi_r2t_rsp - iSCSI R2T Response processing
- * @conn: iscsi connection
- * @task: scsi command task
- **/
-static int
-iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
-{
- struct iscsi_r2t_info *r2t;
- struct iscsi_session *session = conn->session;
- struct iscsi_tcp_task *tcp_task = task->dd_data;
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_r2t_rsp *rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr;
- int r2tsn = be32_to_cpu(rhdr->r2tsn);
- int rc;
-
- if (tcp_conn->in.datalen) {
- iscsi_conn_printk(KERN_ERR, conn,
- "invalid R2t with datalen %d\n",
- tcp_conn->in.datalen);
- return ISCSI_ERR_DATALEN;
- }
-
- if (tcp_task->exp_datasn != r2tsn){
- debug_tcp("%s: task->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
- __func__, tcp_task->exp_datasn, r2tsn);
- return ISCSI_ERR_R2TSN;
- }
-
- /* fill-in new R2T associated with the task */
- iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
-
- if (!task->sc || session->state != ISCSI_STATE_LOGGED_IN) {
- iscsi_conn_printk(KERN_INFO, conn,
- "dropping R2T itt %d in recovery.\n",
- task->itt);
- return 0;
- }
-
- rc = __kfifo_get(tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*));
- BUG_ON(!rc);
-
- r2t->exp_statsn = rhdr->statsn;
- r2t->data_length = be32_to_cpu(rhdr->data_length);
- if (r2t->data_length == 0) {
- iscsi_conn_printk(KERN_ERR, conn,
- "invalid R2T with zero data len\n");
- __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
- return ISCSI_ERR_DATALEN;
- }
-
- if (r2t->data_length > session->max_burst)
- debug_scsi("invalid R2T with data len %u and max burst %u."
- "Attempting to execute request.\n",
- r2t->data_length, session->max_burst);
-
- r2t->data_offset = be32_to_cpu(rhdr->data_offset);
- if (r2t->data_offset + r2t->data_length > scsi_out(task->sc)->length) {
- iscsi_conn_printk(KERN_ERR, conn,
- "invalid R2T with data len %u at offset %u "
- "and total length %d\n", r2t->data_length,
- r2t->data_offset, scsi_out(task->sc)->length);
- __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
- return ISCSI_ERR_DATALEN;
- }
-
- r2t->ttt = rhdr->ttt; /* no flip */
- r2t->solicit_datasn = 0;
-
- iscsi_solicit_data_init(conn, task, r2t);
-
- tcp_task->exp_datasn = r2tsn + 1;
- __kfifo_put(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*));
- conn->r2t_pdus_cnt++;
-
- iscsi_requeue_task(task);
- return 0;
-}
-
-/*
- * Handle incoming reply to DataIn command
- */
-static int
-iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment)
-{
- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
- struct iscsi_hdr *hdr = tcp_conn->in.hdr;
- int rc;
-
- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
- return ISCSI_ERR_DATA_DGST;
-
- /* check for non-exceptional status */
- if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
- rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0);
- if (rc)
- return rc;
- }
-
- iscsi_tcp_hdr_recv_prep(tcp_conn);
- return 0;
-}
-
-/**
- * iscsi_tcp_hdr_dissect - process PDU header
- * @conn: iSCSI connection
- * @hdr: PDU header
- *
- * This function analyzes the header of the PDU received,
- * and performs several sanity checks. If the PDU is accompanied
- * by data, the receive buffer is set up to copy the incoming data
- * to the correct location.
- */
-static int
-iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
-{
- int rc = 0, opcode, ahslen;
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_task *task;
-
- /* verify PDU length */
- tcp_conn->in.datalen = ntoh24(hdr->dlength);
- if (tcp_conn->in.datalen > conn->max_recv_dlength) {
- iscsi_conn_printk(KERN_ERR, conn,
- "iscsi_tcp: datalen %d > %d\n",
- tcp_conn->in.datalen, conn->max_recv_dlength);
- return ISCSI_ERR_DATALEN;
- }
-
- /* Additional header segments. So far, we don't
- * process additional headers.
- */
- ahslen = hdr->hlength << 2;
-
- opcode = hdr->opcode & ISCSI_OPCODE_MASK;
- /* verify itt (itt encoding: age+cid+itt) */
- rc = iscsi_verify_itt(conn, hdr->itt);
- if (rc)
- return rc;
-
- debug_tcp("opcode 0x%x ahslen %d datalen %d\n",
- opcode, ahslen, tcp_conn->in.datalen);
-
- switch(opcode) {
- case ISCSI_OP_SCSI_DATA_IN:
- spin_lock(&conn->session->lock);
- task = iscsi_itt_to_ctask(conn, hdr->itt);
- if (!task)
- rc = ISCSI_ERR_BAD_ITT;
- else
- rc = iscsi_data_in(conn, task);
- if (rc) {
- spin_unlock(&conn->session->lock);
- break;
- }
-
- if (tcp_conn->in.datalen) {
- struct iscsi_tcp_task *tcp_task = task->dd_data;
- struct hash_desc *rx_hash = NULL;
- struct scsi_data_buffer *sdb = scsi_in(task->sc);
-
- /*
- * Setup copy of Data-In into the Scsi_Cmnd
- * Scatterlist case:
- * We set up the iscsi_segment to point to the next
- * scatterlist entry to copy to. As we go along,
- * we move on to the next scatterlist entry and
- * update the digest per-entry.
- */
- if (conn->datadgst_en)
- rx_hash = &tcp_conn->rx_hash;
-
- debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, "
- "datalen=%d)\n", tcp_conn,
- tcp_task->data_offset,
- tcp_conn->in.datalen);
- rc = iscsi_segment_seek_sg(&tcp_conn->in.segment,
- sdb->table.sgl,
- sdb->table.nents,
- tcp_task->data_offset,
- tcp_conn->in.datalen,
- iscsi_tcp_process_data_in,
- rx_hash);
- spin_unlock(&conn->session->lock);
- return rc;
- }
- rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
- spin_unlock(&conn->session->lock);
- break;
- case ISCSI_OP_SCSI_CMD_RSP:
- if (tcp_conn->in.datalen) {
- iscsi_tcp_data_recv_prep(tcp_conn);
- return 0;
- }
- rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
- break;
- case ISCSI_OP_R2T:
- spin_lock(&conn->session->lock);
- task = iscsi_itt_to_ctask(conn, hdr->itt);
- if (!task)
- rc = ISCSI_ERR_BAD_ITT;
- else if (ahslen)
- rc = ISCSI_ERR_AHSLEN;
- else if (task->sc->sc_data_direction == DMA_TO_DEVICE)
- rc = iscsi_r2t_rsp(conn, task);
- else
- rc = ISCSI_ERR_PROTO;
- spin_unlock(&conn->session->lock);
- break;
- case ISCSI_OP_LOGIN_RSP:
- case ISCSI_OP_TEXT_RSP:
- case ISCSI_OP_REJECT:
- case ISCSI_OP_ASYNC_EVENT:
- /*
- * It is possible that we could get a PDU with a buffer larger
- * than 8K, but there are no targets that currently do this.
- * For now we fail until we find a vendor that needs it
- */
- if (ISCSI_DEF_MAX_RECV_SEG_LEN < tcp_conn->in.datalen) {
- iscsi_conn_printk(KERN_ERR, conn,
- "iscsi_tcp: received buffer of "
- "len %u but conn buffer is only %u "
- "(opcode %0x)\n",
- tcp_conn->in.datalen,
- ISCSI_DEF_MAX_RECV_SEG_LEN, opcode);
- rc = ISCSI_ERR_PROTO;
- break;
- }
-
- /* If there's data coming in with the response,
- * receive it to the connection's buffer.
- */
- if (tcp_conn->in.datalen) {
- iscsi_tcp_data_recv_prep(tcp_conn);
- return 0;
- }
- /* fall through */
- case ISCSI_OP_LOGOUT_RSP:
- case ISCSI_OP_NOOP_IN:
- case ISCSI_OP_SCSI_TMFUNC_RSP:
- rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
- break;
- default:
- rc = ISCSI_ERR_BAD_OPCODE;
- break;
- }
-
- if (rc == 0) {
- /* Anything that comes with data should have
- * been handled above. */
- if (tcp_conn->in.datalen)
- return ISCSI_ERR_PROTO;
- iscsi_tcp_hdr_recv_prep(tcp_conn);
- }
-
- return rc;
-}
-
/**
- * iscsi_tcp_hdr_recv_done - process PDU header
- *
- * This is the callback invoked when the PDU header has
- * been received. If the header is followed by additional
- * header segments, we go back for more data.
- */
-static int
-iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment)
-{
- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
- struct iscsi_hdr *hdr;
-
- /* Check if there are additional header segments
- * *prior* to computing the digest, because we
- * may need to go back to the caller for more.
- */
- hdr = (struct iscsi_hdr *) tcp_conn->in.hdr_buf;
- if (segment->copied == sizeof(struct iscsi_hdr) && hdr->hlength) {
- /* Bump the header length - the caller will
- * just loop around and get the AHS for us, and
- * call again. */
- unsigned int ahslen = hdr->hlength << 2;
-
- /* Make sure we don't overflow */
- if (sizeof(*hdr) + ahslen > sizeof(tcp_conn->in.hdr_buf))
- return ISCSI_ERR_AHSLEN;
-
- segment->total_size += ahslen;
- segment->size += ahslen;
- return 0;
- }
-
- /* We're done processing the header. See if we're doing
- * header digests; if so, set up the recv_digest buffer
- * and go back for more. */
- if (conn->hdrdgst_en) {
- if (segment->digest_len == 0) {
- iscsi_tcp_segment_splice_digest(segment,
- segment->recv_digest);
- return 0;
- }
- iscsi_tcp_dgst_header(&tcp_conn->rx_hash, hdr,
- segment->total_copied - ISCSI_DIGEST_SIZE,
- segment->digest);
-
- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
- return ISCSI_ERR_HDR_DGST;
- }
-
- tcp_conn->in.hdr = hdr;
- return iscsi_tcp_hdr_dissect(conn, hdr);
-}
-
-/**
- * iscsi_tcp_recv - TCP receive in sendfile fashion
+ * iscsi_sw_tcp_recv - TCP receive in sendfile fashion
* @rd_desc: read descriptor
* @skb: socket buffer
* @offset: offset in skb
* @len: skb->len - offset
- **/
-static int
-iscsi_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
- unsigned int offset, size_t len)
+ */
+static int iscsi_sw_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
+ unsigned int offset, size_t len)
{
struct iscsi_conn *conn = rd_desc->arg.data;
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_segment *segment = &tcp_conn->in.segment;
- struct skb_seq_state seq;
- unsigned int consumed = 0;
- int rc = 0;
+ unsigned int consumed, total_consumed = 0;
+ int status;
debug_tcp("in %d bytes\n", skb->len - offset);
- if (unlikely(conn->suspend_rx)) {
- debug_tcp("conn %d Rx suspended!\n", conn->id);
- return 0;
- }
+ do {
+ status = 0;
+ consumed = iscsi_tcp_recv_skb(conn, skb, offset, 0, &status);
+ offset += consumed;
+ total_consumed += consumed;
+ } while (consumed != 0 && status != ISCSI_TCP_SKB_DONE);
- skb_prepare_seq_read(skb, offset, skb->len, &seq);
- while (1) {
- unsigned int avail;
- const u8 *ptr;
-
- avail = skb_seq_read(consumed, &ptr, &seq);
- if (avail == 0) {
- debug_tcp("no more data avail. Consumed %d\n",
- consumed);
- break;
- }
- BUG_ON(segment->copied >= segment->size);
-
- debug_tcp("skb %p ptr=%p avail=%u\n", skb, ptr, avail);
- rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr, avail);
- BUG_ON(rc == 0);
- consumed += rc;
-
- if (segment->total_copied >= segment->total_size) {
- debug_tcp("segment done\n");
- rc = segment->done(tcp_conn, segment);
- if (rc != 0) {
- skb_abort_seq_read(&seq);
- goto error;
- }
-
- /* The done() functions sets up the
- * next segment. */
- }
- }
- skb_abort_seq_read(&seq);
- conn->rxdata_octets += consumed;
- return consumed;
-
-error:
- debug_tcp("Error receiving PDU, errno=%d\n", rc);
- iscsi_conn_failure(conn, rc);
- return 0;
+ debug_tcp("read %d bytes status %d\n", skb->len - offset, status);
+ return total_consumed;
}
-static void
-iscsi_tcp_data_ready(struct sock *sk, int flag)
+static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag)
{
struct iscsi_conn *conn = sk->sk_user_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
@@ -1000,7 +106,7 @@ iscsi_tcp_data_ready(struct sock *sk, int flag)
*/
rd_desc.arg.data = conn;
rd_desc.count = 1;
- tcp_read_sock(sk, &rd_desc, iscsi_tcp_recv);
+ tcp_read_sock(sk, &rd_desc, iscsi_sw_tcp_recv);
read_unlock(&sk->sk_callback_lock);
@@ -1009,10 +115,10 @@ iscsi_tcp_data_ready(struct sock *sk, int flag)
iscsi_tcp_segment_unmap(&tcp_conn->in.segment);
}
-static void
-iscsi_tcp_state_change(struct sock *sk)
+static void iscsi_sw_tcp_state_change(struct sock *sk)
{
struct iscsi_tcp_conn *tcp_conn;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn;
struct iscsi_conn *conn;
struct iscsi_session *session;
void (*old_state_change)(struct sock *);
@@ -1030,7 +136,8 @@ iscsi_tcp_state_change(struct sock *sk)
}
tcp_conn = conn->dd_data;
- old_state_change = tcp_conn->old_state_change;
+ tcp_sw_conn = tcp_conn->dd_data;
+ old_state_change = tcp_sw_conn->old_state_change;
read_unlock(&sk->sk_callback_lock);
@@ -1041,63 +148,123 @@ iscsi_tcp_state_change(struct sock *sk)
* iscsi_write_space - Called when more output buffer space is available
* @sk: socket space is available for
**/
-static void
-iscsi_write_space(struct sock *sk)
+static void iscsi_sw_tcp_write_space(struct sock *sk)
{
struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
- tcp_conn->old_write_space(sk);
+ tcp_sw_conn->old_write_space(sk);
debug_tcp("iscsi_write_space: cid %d\n", conn->id);
scsi_queue_work(conn->session->host, &conn->xmitwork);
}
-static void
-iscsi_conn_set_callbacks(struct iscsi_conn *conn)
+static void iscsi_sw_tcp_conn_set_callbacks(struct iscsi_conn *conn)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct sock *sk = tcp_conn->sock->sk;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct sock *sk = tcp_sw_conn->sock->sk;
/* assign new callbacks */
write_lock_bh(&sk->sk_callback_lock);
sk->sk_user_data = conn;
- tcp_conn->old_data_ready = sk->sk_data_ready;
- tcp_conn->old_state_change = sk->sk_state_change;
- tcp_conn->old_write_space = sk->sk_write_space;
- sk->sk_data_ready = iscsi_tcp_data_ready;
- sk->sk_state_change = iscsi_tcp_state_change;
- sk->sk_write_space = iscsi_write_space;
+ tcp_sw_conn->old_data_ready = sk->sk_data_ready;
+ tcp_sw_conn->old_state_change = sk->sk_state_change;
+ tcp_sw_conn->old_write_space = sk->sk_write_space;
+ sk->sk_data_ready = iscsi_sw_tcp_data_ready;
+ sk->sk_state_change = iscsi_sw_tcp_state_change;
+ sk->sk_write_space = iscsi_sw_tcp_write_space;
write_unlock_bh(&sk->sk_callback_lock);
}
static void
-iscsi_conn_restore_callbacks(struct iscsi_tcp_conn *tcp_conn)
+iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_sw_tcp_conn *tcp_sw_conn)
{
- struct sock *sk = tcp_conn->sock->sk;
+ struct sock *sk = tcp_sw_conn->sock->sk;
/* restore socket callbacks, see also: iscsi_conn_set_callbacks() */
write_lock_bh(&sk->sk_callback_lock);
sk->sk_user_data = NULL;
- sk->sk_data_ready = tcp_conn->old_data_ready;
- sk->sk_state_change = tcp_conn->old_state_change;
- sk->sk_write_space = tcp_conn->old_write_space;
+ sk->sk_data_ready = tcp_sw_conn->old_data_ready;
+ sk->sk_state_change = tcp_sw_conn->old_state_change;
+ sk->sk_write_space = tcp_sw_conn->old_write_space;
sk->sk_no_check = 0;
write_unlock_bh(&sk->sk_callback_lock);
}
/**
- * iscsi_xmit - TCP transmit
+ * iscsi_sw_tcp_xmit_segment - transmit segment
+ * @tcp_conn: the iSCSI TCP connection
+ * @segment: the buffer to transmnit
+ *
+ * This function transmits as much of the buffer as
+ * the network layer will accept, and returns the number of
+ * bytes transmitted.
+ *
+ * If CRC hashing is enabled, the function will compute the
+ * hash as it goes. When the entire segment has been transmitted,
+ * it will retrieve the hash value and send it as well.
+ */
+static int iscsi_sw_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment)
+{
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct socket *sk = tcp_sw_conn->sock;
+ unsigned int copied = 0;
+ int r = 0;
+
+ while (!iscsi_tcp_segment_done(tcp_conn, segment, 0, r)) {
+ struct scatterlist *sg;
+ unsigned int offset, copy;
+ int flags = 0;
+
+ r = 0;
+ offset = segment->copied;
+ copy = segment->size - offset;
+
+ if (segment->total_copied + segment->size < segment->total_size)
+ flags |= MSG_MORE;
+
+ /* Use sendpage if we can; else fall back to sendmsg */
+ if (!segment->data) {
+ sg = segment->sg;
+ offset += segment->sg_offset + sg->offset;
+ r = tcp_sw_conn->sendpage(sk, sg_page(sg), offset,
+ copy, flags);
+ } else {
+ struct msghdr msg = { .msg_flags = flags };
+ struct kvec iov = {
+ .iov_base = segment->data + offset,
+ .iov_len = copy
+ };
+
+ r = kernel_sendmsg(sk, &msg, &iov, 1, copy);
+ }
+
+ if (r < 0) {
+ iscsi_tcp_segment_unmap(segment);
+ if (copied || r == -EAGAIN)
+ break;
+ return r;
+ }
+ copied += r;
+ }
+ return copied;
+}
+
+/**
+ * iscsi_sw_tcp_xmit - TCP transmit
**/
-static int
-iscsi_xmit(struct iscsi_conn *conn)
+static int iscsi_sw_tcp_xmit(struct iscsi_conn *conn)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_segment *segment = &tcp_conn->out.segment;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct iscsi_segment *segment = &tcp_sw_conn->out.segment;
unsigned int consumed = 0;
int rc = 0;
while (1) {
- rc = iscsi_tcp_xmit_segment(tcp_conn, segment);
+ rc = iscsi_sw_tcp_xmit_segment(tcp_conn, segment);
if (rc < 0) {
rc = ISCSI_ERR_XMIT_FAILED;
goto error;
@@ -1132,22 +299,22 @@ error:
/**
* iscsi_tcp_xmit_qlen - return the number of bytes queued for xmit
*/
-static inline int
-iscsi_tcp_xmit_qlen(struct iscsi_conn *conn)
+static inline int iscsi_sw_tcp_xmit_qlen(struct iscsi_conn *conn)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_segment *segment = &tcp_conn->out.segment;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct iscsi_segment *segment = &tcp_sw_conn->out.segment;
return segment->total_copied - segment->total_size;
}
-static inline int
-iscsi_tcp_flush(struct iscsi_conn *conn)
+static int iscsi_sw_tcp_pdu_xmit(struct iscsi_task *task)
{
+ struct iscsi_conn *conn = task->conn;
int rc;
- while (iscsi_tcp_xmit_qlen(conn)) {
- rc = iscsi_xmit(conn);
+ while (iscsi_sw_tcp_xmit_qlen(conn)) {
+ rc = iscsi_sw_tcp_xmit(conn);
if (rc == 0)
return -EAGAIN;
if (rc < 0)
@@ -1161,27 +328,31 @@ iscsi_tcp_flush(struct iscsi_conn *conn)
* This is called when we're done sending the header.
* Simply copy the data_segment to the send segment, and return.
*/
-static int
-iscsi_tcp_send_hdr_done(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment)
+static int iscsi_sw_tcp_send_hdr_done(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment)
{
- tcp_conn->out.segment = tcp_conn->out.data_segment;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+
+ tcp_sw_conn->out.segment = tcp_sw_conn->out.data_segment;
debug_tcp("Header done. Next segment size %u total_size %u\n",
- tcp_conn->out.segment.size, tcp_conn->out.segment.total_size);
+ tcp_sw_conn->out.segment.size,
+ tcp_sw_conn->out.segment.total_size);
return 0;
}
-static void
-iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
+static void iscsi_sw_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr,
+ size_t hdrlen)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
debug_tcp("%s(%p%s)\n", __func__, tcp_conn,
conn->hdrdgst_en? ", digest enabled" : "");
/* Clear the data segment - needs to be filled in by the
* caller using iscsi_tcp_send_data_prep() */
- memset(&tcp_conn->out.data_segment, 0, sizeof(struct iscsi_segment));
+ memset(&tcp_sw_conn->out.data_segment, 0,
+ sizeof(struct iscsi_segment));
/* If header digest is enabled, compute the CRC and
* place the digest into the same buffer. We make
@@ -1189,7 +360,7 @@ iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
* sufficient room.
*/
if (conn->hdrdgst_en) {
- iscsi_tcp_dgst_header(&tcp_conn->tx_hash, hdr, hdrlen,
+ iscsi_tcp_dgst_header(&tcp_sw_conn->tx_hash, hdr, hdrlen,
hdr + hdrlen);
hdrlen += ISCSI_DIGEST_SIZE;
}
@@ -1197,10 +368,10 @@ iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
/* Remember header pointer for later, when we need
* to decide whether there's a payload to go along
* with the header. */
- tcp_conn->out.hdr = hdr;
+ tcp_sw_conn->out.hdr = hdr;
- iscsi_segment_init_linear(&tcp_conn->out.segment, hdr, hdrlen,
- iscsi_tcp_send_hdr_done, NULL);
+ iscsi_segment_init_linear(&tcp_sw_conn->out.segment, hdr, hdrlen,
+ iscsi_sw_tcp_send_hdr_done, NULL);
}
/*
@@ -1209,11 +380,12 @@ iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
* of by the iscsi_segment routines.
*/
static int
-iscsi_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
- unsigned int count, unsigned int offset,
- unsigned int len)
+iscsi_sw_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
+ unsigned int count, unsigned int offset,
+ unsigned int len)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
struct hash_desc *tx_hash = NULL;
unsigned int hdr_spec_len;
@@ -1223,22 +395,23 @@ iscsi_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
/* Make sure the datalen matches what the caller
said he would send. */
- hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength);
+ hdr_spec_len = ntoh24(tcp_sw_conn->out.hdr->dlength);
WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
if (conn->datadgst_en)
- tx_hash = &tcp_conn->tx_hash;
+ tx_hash = &tcp_sw_conn->tx_hash;
- return iscsi_segment_seek_sg(&tcp_conn->out.data_segment,
- sg, count, offset, len,
- NULL, tx_hash);
+ return iscsi_segment_seek_sg(&tcp_sw_conn->out.data_segment,
+ sg, count, offset, len,
+ NULL, tx_hash);
}
static void
-iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
+iscsi_sw_tcp_send_linear_data_prep(struct iscsi_conn *conn, void *data,
size_t len)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
struct hash_desc *tx_hash = NULL;
unsigned int hdr_spec_len;
@@ -1247,341 +420,160 @@ iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
/* Make sure the datalen matches what the caller
said he would send. */
- hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength);
+ hdr_spec_len = ntoh24(tcp_sw_conn->out.hdr->dlength);
WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
if (conn->datadgst_en)
- tx_hash = &tcp_conn->tx_hash;
+ tx_hash = &tcp_sw_conn->tx_hash;
- iscsi_segment_init_linear(&tcp_conn->out.data_segment,
+ iscsi_segment_init_linear(&tcp_sw_conn->out.data_segment,
data, len, NULL, tx_hash);
}
-/**
- * iscsi_solicit_data_cont - initialize next Data-Out
- * @conn: iscsi connection
- * @task: scsi command task
- * @r2t: R2T info
- * @left: bytes left to transfer
- *
- * Notes:
- * Initialize next Data-Out within this R2T sequence and continue
- * to process next Scatter-Gather element(if any) of this SCSI command.
- *
- * Called under connection lock.
- **/
-static int
-iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_task *task,
- struct iscsi_r2t_info *r2t)
+static int iscsi_sw_tcp_pdu_init(struct iscsi_task *task,
+ unsigned int offset, unsigned int count)
{
- struct iscsi_data *hdr;
- int new_offset, left;
-
- BUG_ON(r2t->data_length - r2t->sent < 0);
- left = r2t->data_length - r2t->sent;
- if (left == 0)
- return 0;
-
- hdr = &r2t->dtask.hdr;
- memset(hdr, 0, sizeof(struct iscsi_data));
- hdr->ttt = r2t->ttt;
- hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
- r2t->solicit_datasn++;
- hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
- memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
- hdr->itt = task->hdr->itt;
- hdr->exp_statsn = r2t->exp_statsn;
- new_offset = r2t->data_offset + r2t->sent;
- hdr->offset = cpu_to_be32(new_offset);
- if (left > conn->max_xmit_dlength) {
- hton24(hdr->dlength, conn->max_xmit_dlength);
- r2t->data_count = conn->max_xmit_dlength;
- } else {
- hton24(hdr->dlength, left);
- r2t->data_count = left;
- hdr->flags = ISCSI_FLAG_CMD_FINAL;
- }
-
- conn->dataout_pdus_cnt++;
- return 1;
-}
-
-/**
- * iscsi_tcp_task - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
- * @conn: iscsi connection
- * @task: scsi command task
- * @sc: scsi command
- **/
-static int
-iscsi_tcp_task_init(struct iscsi_task *task)
-{
- struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_conn *conn = task->conn;
- struct scsi_cmnd *sc = task->sc;
- int err;
+ int err = 0;
- if (!sc) {
- /*
- * mgmt tasks do not have a scatterlist since they come
- * in from the iscsi interface.
- */
- debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id,
- task->itt);
-
- /* Prepare PDU, optionally w/ immediate data */
- iscsi_tcp_send_hdr_prep(conn, task->hdr, sizeof(*task->hdr));
-
- /* If we have immediate data, attach a payload */
- if (task->data_count)
- iscsi_tcp_send_linear_data_prepare(conn, task->data,
- task->data_count);
- return 0;
- }
+ iscsi_sw_tcp_send_hdr_prep(conn, task->hdr, task->hdr_len);
- BUG_ON(__kfifo_len(tcp_task->r2tqueue));
- tcp_task->sent = 0;
- tcp_task->exp_datasn = 0;
+ if (!count)
+ return 0;
- /* Prepare PDU, optionally w/ immediate data */
- debug_scsi("task deq [cid %d itt 0x%x imm %d unsol %d]\n",
- conn->id, task->itt, task->imm_count,
- task->unsol_count);
- iscsi_tcp_send_hdr_prep(conn, task->hdr, task->hdr_len);
+ if (!task->sc)
+ iscsi_sw_tcp_send_linear_data_prep(conn, task->data, count);
+ else {
+ struct scsi_data_buffer *sdb = scsi_out(task->sc);
- if (!task->imm_count)
- return 0;
+ err = iscsi_sw_tcp_send_data_prep(conn, sdb->table.sgl,
+ sdb->table.nents, offset,
+ count);
+ }
- /* If we have immediate data, attach a payload */
- err = iscsi_tcp_send_data_prep(conn, scsi_out(sc)->table.sgl,
- scsi_out(sc)->table.nents,
- 0, task->imm_count);
- if (err)
- return err;
- tcp_task->sent += task->imm_count;
- task->imm_count = 0;
+ if (err) {
+ iscsi_conn_failure(conn, err);
+ return -EIO;
+ }
return 0;
}
-/*
- * iscsi_tcp_task_xmit - xmit normal PDU task
- * @task: iscsi command task
- *
- * We're expected to return 0 when everything was transmitted succesfully,
- * -EAGAIN if there's still data in the queue, or != 0 for any other kind
- * of error.
- */
-static int
-iscsi_tcp_task_xmit(struct iscsi_task *task)
+static int iscsi_sw_tcp_pdu_alloc(struct iscsi_task *task, uint8_t opcode)
{
- struct iscsi_conn *conn = task->conn;
struct iscsi_tcp_task *tcp_task = task->dd_data;
- struct scsi_cmnd *sc = task->sc;
- struct scsi_data_buffer *sdb;
- int rc = 0;
-
-flush:
- /* Flush any pending data first. */
- rc = iscsi_tcp_flush(conn);
- if (rc < 0)
- return rc;
-
- /* mgmt command */
- if (!sc) {
- if (task->hdr->itt == RESERVED_ITT)
- iscsi_put_task(task);
- return 0;
- }
-
- /* Are we done already? */
- if (sc->sc_data_direction != DMA_TO_DEVICE)
- return 0;
- sdb = scsi_out(sc);
- if (task->unsol_count != 0) {
- struct iscsi_data *hdr = &tcp_task->unsol_dtask.hdr;
-
- /* Prepare a header for the unsolicited PDU.
- * The amount of data we want to send will be
- * in task->data_count.
- * FIXME: return the data count instead.
- */
- iscsi_prep_unsolicit_data_pdu(task, hdr);
-
- debug_tcp("unsol dout [itt 0x%x doff %d dlen %d]\n",
- task->itt, tcp_task->sent, task->data_count);
-
- iscsi_tcp_send_hdr_prep(conn, hdr, sizeof(*hdr));
- rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
- sdb->table.nents, tcp_task->sent,
- task->data_count);
- if (rc)
- goto fail;
- tcp_task->sent += task->data_count;
- task->unsol_count -= task->data_count;
- goto flush;
- } else {
- struct iscsi_session *session = conn->session;
- struct iscsi_r2t_info *r2t;
-
- /* All unsolicited PDUs sent. Check for solicited PDUs.
- */
- spin_lock_bh(&session->lock);
- r2t = tcp_task->r2t;
- if (r2t != NULL) {
- /* Continue with this R2T? */
- if (!iscsi_solicit_data_cont(conn, task, r2t)) {
- debug_scsi(" done with r2t %p\n", r2t);
-
- __kfifo_put(tcp_task->r2tpool.queue,
- (void*)&r2t, sizeof(void*));
- tcp_task->r2t = r2t = NULL;
- }
- }
-
- if (r2t == NULL) {
- __kfifo_get(tcp_task->r2tqueue, (void*)&tcp_task->r2t,
- sizeof(void*));
- r2t = tcp_task->r2t;
- }
- spin_unlock_bh(&session->lock);
-
- /* Waiting for more R2Ts to arrive. */
- if (r2t == NULL) {
- debug_tcp("no R2Ts yet\n");
- return 0;
- }
-
- debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n",
- r2t, r2t->solicit_datasn - 1, task->itt,
- r2t->data_offset + r2t->sent, r2t->data_count);
-
- iscsi_tcp_send_hdr_prep(conn, &r2t->dtask.hdr,
- sizeof(struct iscsi_hdr));
-
- rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
- sdb->table.nents,
- r2t->data_offset + r2t->sent,
- r2t->data_count);
- if (rc)
- goto fail;
- tcp_task->sent += r2t->data_count;
- r2t->sent += r2t->data_count;
- goto flush;
- }
+ task->hdr = task->dd_data + sizeof(*tcp_task);
+ task->hdr_max = sizeof(struct iscsi_sw_tcp_hdrbuf) - ISCSI_DIGEST_SIZE;
return 0;
-fail:
- iscsi_conn_failure(conn, rc);
- return -EIO;
}
static struct iscsi_cls_conn *
-iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+iscsi_sw_tcp_conn_create(struct iscsi_cls_session *cls_session,
+ uint32_t conn_idx)
{
struct iscsi_conn *conn;
struct iscsi_cls_conn *cls_conn;
struct iscsi_tcp_conn *tcp_conn;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn;
- cls_conn = iscsi_conn_setup(cls_session, sizeof(*tcp_conn), conn_idx);
+ cls_conn = iscsi_tcp_conn_setup(cls_session, sizeof(*tcp_sw_conn),
+ conn_idx);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
- /*
- * due to strange issues with iser these are not set
- * in iscsi_conn_setup
- */
- conn->max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
-
tcp_conn = conn->dd_data;
- tcp_conn->iscsi_conn = conn;
+ tcp_sw_conn = tcp_conn->dd_data;
- tcp_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
- CRYPTO_ALG_ASYNC);
- tcp_conn->tx_hash.flags = 0;
- if (IS_ERR(tcp_conn->tx_hash.tfm))
+ tcp_sw_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
+ CRYPTO_ALG_ASYNC);
+ tcp_sw_conn->tx_hash.flags = 0;
+ if (IS_ERR(tcp_sw_conn->tx_hash.tfm))
goto free_conn;
- tcp_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
- CRYPTO_ALG_ASYNC);
- tcp_conn->rx_hash.flags = 0;
- if (IS_ERR(tcp_conn->rx_hash.tfm))
+ tcp_sw_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
+ CRYPTO_ALG_ASYNC);
+ tcp_sw_conn->rx_hash.flags = 0;
+ if (IS_ERR(tcp_sw_conn->rx_hash.tfm))
goto free_tx_tfm;
+ tcp_conn->rx_hash = &tcp_sw_conn->rx_hash;
return cls_conn;
free_tx_tfm:
- crypto_free_hash(tcp_conn->tx_hash.tfm);
+ crypto_free_hash(tcp_sw_conn->tx_hash.tfm);
free_conn:
iscsi_conn_printk(KERN_ERR, conn,
"Could not create connection due to crc32c "
"loading error. Make sure the crc32c "
"module is built as a module or into the "
"kernel\n");
- iscsi_conn_teardown(cls_conn);
+ iscsi_tcp_conn_teardown(cls_conn);
return NULL;
}
-static void
-iscsi_tcp_release_conn(struct iscsi_conn *conn)
+static void iscsi_sw_tcp_release_conn(struct iscsi_conn *conn)
{
struct iscsi_session *session = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct socket *sock = tcp_conn->sock;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct socket *sock = tcp_sw_conn->sock;
if (!sock)
return;
sock_hold(sock->sk);
- iscsi_conn_restore_callbacks(tcp_conn);
+ iscsi_sw_tcp_conn_restore_callbacks(tcp_sw_conn);
sock_put(sock->sk);
spin_lock_bh(&session->lock);
- tcp_conn->sock = NULL;
+ tcp_sw_conn->sock = NULL;
spin_unlock_bh(&session->lock);
sockfd_put(sock);
}
-static void
-iscsi_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn)
+static void iscsi_sw_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
- iscsi_tcp_release_conn(conn);
+ iscsi_sw_tcp_release_conn(conn);
- if (tcp_conn->tx_hash.tfm)
- crypto_free_hash(tcp_conn->tx_hash.tfm);
- if (tcp_conn->rx_hash.tfm)
- crypto_free_hash(tcp_conn->rx_hash.tfm);
+ if (tcp_sw_conn->tx_hash.tfm)
+ crypto_free_hash(tcp_sw_conn->tx_hash.tfm);
+ if (tcp_sw_conn->rx_hash.tfm)
+ crypto_free_hash(tcp_sw_conn->rx_hash.tfm);
- iscsi_conn_teardown(cls_conn);
+ iscsi_tcp_conn_teardown(cls_conn);
}
-static void
-iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+static void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
/* userspace may have goofed up and not bound us */
- if (!tcp_conn->sock)
+ if (!tcp_sw_conn->sock)
return;
/*
* Make sure our recv side is stopped.
* Older tools called conn stop before ep_disconnect
* so IO could still be coming in.
*/
- write_lock_bh(&tcp_conn->sock->sk->sk_callback_lock);
+ write_lock_bh(&tcp_sw_conn->sock->sk->sk_callback_lock);
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
- write_unlock_bh(&tcp_conn->sock->sk->sk_callback_lock);
+ write_unlock_bh(&tcp_sw_conn->sock->sk->sk_callback_lock);
iscsi_conn_stop(cls_conn, flag);
- iscsi_tcp_release_conn(conn);
+ iscsi_sw_tcp_release_conn(conn);
}
-static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock,
- char *buf, int *port,
- int (*getname)(struct socket *, struct sockaddr *,
- int *addrlen))
+static int iscsi_sw_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock,
+ char *buf, int *port,
+ int (*getname)(struct socket *,
+ struct sockaddr *,
+ int *addrlen))
{
struct sockaddr_storage *addr;
struct sockaddr_in6 *sin6;
@@ -1619,14 +611,15 @@ free_addr:
}
static int
-iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
- struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
- int is_leading)
+iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session,
+ struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
+ int is_leading)
{
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
struct iscsi_host *ihost = shost_priv(shost);
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
struct sock *sk;
struct socket *sock;
int err;
@@ -1643,13 +636,13 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
* userspace may still want to query the values since we will
* be using them for the reconnect
*/
- err = iscsi_tcp_get_addr(conn, sock, conn->portal_address,
- &conn->portal_port, kernel_getpeername);
+ err = iscsi_sw_tcp_get_addr(conn, sock, conn->portal_address,
+ &conn->portal_port, kernel_getpeername);
if (err)
goto free_socket;
- err = iscsi_tcp_get_addr(conn, sock, ihost->local_address,
- &ihost->local_port, kernel_getsockname);
+ err = iscsi_sw_tcp_get_addr(conn, sock, ihost->local_address,
+ &ihost->local_port, kernel_getsockname);
if (err)
goto free_socket;
@@ -1658,7 +651,7 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
goto free_socket;
/* bind iSCSI connection and socket */
- tcp_conn->sock = sock;
+ tcp_sw_conn->sock = sock;
/* setup Socket parameters */
sk = sock->sk;
@@ -1666,8 +659,8 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
sk->sk_allocation = GFP_ATOMIC;
- iscsi_conn_set_callbacks(conn);
- tcp_conn->sendpage = tcp_conn->sock->ops->sendpage;
+ iscsi_sw_tcp_conn_set_callbacks(conn);
+ tcp_sw_conn->sendpage = tcp_sw_conn->sock->ops->sendpage;
/*
* set receive state machine into initial state
*/
@@ -1679,74 +672,14 @@ free_socket:
return err;
}
-static int
-iscsi_r2tpool_alloc(struct iscsi_session *session)
-{
- int i;
- int cmd_i;
-
- /*
- * initialize per-task: R2T pool and xmit queue
- */
- for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
- struct iscsi_task *task = session->cmds[cmd_i];
- struct iscsi_tcp_task *tcp_task = task->dd_data;
-
- /*
- * pre-allocated x4 as much r2ts to handle race when
- * target acks DataOut faster than we data_xmit() queues
- * could replenish r2tqueue.
- */
-
- /* R2T pool */
- if (iscsi_pool_init(&tcp_task->r2tpool, session->max_r2t * 4, NULL,
- sizeof(struct iscsi_r2t_info))) {
- goto r2t_alloc_fail;
- }
-
- /* R2T xmit queue */
- tcp_task->r2tqueue = kfifo_alloc(
- session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL);
- if (tcp_task->r2tqueue == ERR_PTR(-ENOMEM)) {
- iscsi_pool_free(&tcp_task->r2tpool);
- goto r2t_alloc_fail;
- }
- }
-
- return 0;
-
-r2t_alloc_fail:
- for (i = 0; i < cmd_i; i++) {
- struct iscsi_task *task = session->cmds[i];
- struct iscsi_tcp_task *tcp_task = task->dd_data;
-
- kfifo_free(tcp_task->r2tqueue);
- iscsi_pool_free(&tcp_task->r2tpool);
- }
- return -ENOMEM;
-}
-
-static void
-iscsi_r2tpool_free(struct iscsi_session *session)
-{
- int i;
-
- for (i = 0; i < session->cmds_max; i++) {
- struct iscsi_task *task = session->cmds[i];
- struct iscsi_tcp_task *tcp_task = task->dd_data;
-
- kfifo_free(tcp_task->r2tqueue);
- iscsi_pool_free(&tcp_task->r2tpool);
- }
-}
-
-static int
-iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
- char *buf, int buflen)
+static int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf,
+ int buflen)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_session *session = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
int value;
switch(param) {
@@ -1755,8 +688,8 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
break;
case ISCSI_PARAM_DATADGST_EN:
iscsi_set_param(cls_conn, param, buf, buflen);
- tcp_conn->sendpage = conn->datadgst_en ?
- sock_no_sendpage : tcp_conn->sock->ops->sendpage;
+ tcp_sw_conn->sendpage = conn->datadgst_en ?
+ sock_no_sendpage : tcp_sw_conn->sock->ops->sendpage;
break;
case ISCSI_PARAM_MAX_R2T:
sscanf(buf, "%d", &value);
@@ -1764,9 +697,9 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
return -EINVAL;
if (session->max_r2t == value)
break;
- iscsi_r2tpool_free(session);
+ iscsi_tcp_r2tpool_free(session);
iscsi_set_param(cls_conn, param, buf, buflen);
- if (iscsi_r2tpool_alloc(session))
+ if (iscsi_tcp_r2tpool_alloc(session))
return -ENOMEM;
break;
default:
@@ -1776,9 +709,8 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
return 0;
}
-static int
-iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
- enum iscsi_param param, char *buf)
+static int iscsi_sw_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf)
{
struct iscsi_conn *conn = cls_conn->dd_data;
int len;
@@ -1802,48 +734,42 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
}
static void
-iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
+iscsi_sw_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn,
+ struct iscsi_stats *stats)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
- stats->txdata_octets = conn->txdata_octets;
- stats->rxdata_octets = conn->rxdata_octets;
- stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
- stats->dataout_pdus = conn->dataout_pdus_cnt;
- stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
- stats->datain_pdus = conn->datain_pdus_cnt;
- stats->r2t_pdus = conn->r2t_pdus_cnt;
- stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
- stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
stats->custom_length = 3;
strcpy(stats->custom[0].desc, "tx_sendpage_failures");
- stats->custom[0].value = tcp_conn->sendpage_failures_cnt;
+ stats->custom[0].value = tcp_sw_conn->sendpage_failures_cnt;
strcpy(stats->custom[1].desc, "rx_discontiguous_hdr");
- stats->custom[1].value = tcp_conn->discontiguous_hdr_cnt;
+ stats->custom[1].value = tcp_sw_conn->discontiguous_hdr_cnt;
strcpy(stats->custom[2].desc, "eh_abort_cnt");
stats->custom[2].value = conn->eh_abort_cnt;
+
+ iscsi_tcp_conn_get_stats(cls_conn, stats);
}
static struct iscsi_cls_session *
-iscsi_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
- uint16_t qdepth, uint32_t initial_cmdsn,
- uint32_t *hostno)
+iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
+ uint16_t qdepth, uint32_t initial_cmdsn,
+ uint32_t *hostno)
{
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
struct Scsi_Host *shost;
- int cmd_i;
if (ep) {
printk(KERN_ERR "iscsi_tcp: invalid ep %p.\n", ep);
return NULL;
}
- shost = iscsi_host_alloc(&iscsi_sht, 0, qdepth);
+ shost = iscsi_host_alloc(&iscsi_sw_tcp_sht, 0, qdepth);
if (!shost)
return NULL;
- shost->transportt = iscsi_tcp_scsi_transport;
+ shost->transportt = iscsi_sw_tcp_scsi_transport;
shost->max_lun = iscsi_max_lun;
shost->max_id = 0;
shost->max_channel = 0;
@@ -1853,23 +779,17 @@ iscsi_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
goto free_host;
*hostno = shost->host_no;
- cls_session = iscsi_session_setup(&iscsi_tcp_transport, shost, cmds_max,
- sizeof(struct iscsi_tcp_task),
+ cls_session = iscsi_session_setup(&iscsi_sw_tcp_transport, shost,
+ cmds_max,
+ sizeof(struct iscsi_tcp_task) +
+ sizeof(struct iscsi_sw_tcp_hdrbuf),
initial_cmdsn, 0);
if (!cls_session)
goto remove_host;
session = cls_session->dd_data;
shost->can_queue = session->scsi_cmds_max;
- for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
- struct iscsi_task *task = session->cmds[cmd_i];
- struct iscsi_tcp_task *tcp_task = task->dd_data;
-
- task->hdr = &tcp_task->hdr.cmd_hdr;
- task->hdr_max = sizeof(tcp_task->hdr) - ISCSI_DIGEST_SIZE;
- }
-
- if (iscsi_r2tpool_alloc(session))
+ if (iscsi_tcp_r2tpool_alloc(session))
goto remove_session;
return cls_session;
@@ -1882,25 +802,25 @@ free_host:
return NULL;
}
-static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session)
+static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session)
{
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
- iscsi_r2tpool_free(cls_session->dd_data);
+ iscsi_tcp_r2tpool_free(cls_session->dd_data);
iscsi_session_teardown(cls_session);
iscsi_host_remove(shost);
iscsi_host_free(shost);
}
-static int iscsi_tcp_slave_configure(struct scsi_device *sdev)
+static int iscsi_sw_tcp_slave_configure(struct scsi_device *sdev)
{
blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_ANY);
blk_queue_dma_alignment(sdev->request_queue, 0);
return 0;
}
-static struct scsi_host_template iscsi_sht = {
+static struct scsi_host_template iscsi_sw_tcp_sht = {
.module = THIS_MODULE,
.name = "iSCSI Initiator over TCP/IP",
.queuecommand = iscsi_queuecommand,
@@ -1913,12 +833,12 @@ static struct scsi_host_template iscsi_sht = {
.eh_device_reset_handler= iscsi_eh_device_reset,
.eh_target_reset_handler= iscsi_eh_target_reset,
.use_clustering = DISABLE_CLUSTERING,
- .slave_configure = iscsi_tcp_slave_configure,
+ .slave_configure = iscsi_sw_tcp_slave_configure,
.proc_name = "iscsi_tcp",
.this_id = -1,
};
-static struct iscsi_transport iscsi_tcp_transport = {
+static struct iscsi_transport iscsi_sw_tcp_transport = {
.owner = THIS_MODULE,
.name = "tcp",
.caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
@@ -1951,32 +871,36 @@ static struct iscsi_transport iscsi_tcp_transport = {
ISCSI_HOST_INITIATOR_NAME |
ISCSI_HOST_NETDEV_NAME,
/* session management */
- .create_session = iscsi_tcp_session_create,
- .destroy_session = iscsi_tcp_session_destroy,
+ .create_session = iscsi_sw_tcp_session_create,
+ .destroy_session = iscsi_sw_tcp_session_destroy,
/* connection management */
- .create_conn = iscsi_tcp_conn_create,
- .bind_conn = iscsi_tcp_conn_bind,
- .destroy_conn = iscsi_tcp_conn_destroy,
- .set_param = iscsi_conn_set_param,
- .get_conn_param = iscsi_tcp_conn_get_param,
+ .create_conn = iscsi_sw_tcp_conn_create,
+ .bind_conn = iscsi_sw_tcp_conn_bind,
+ .destroy_conn = iscsi_sw_tcp_conn_destroy,
+ .set_param = iscsi_sw_tcp_conn_set_param,
+ .get_conn_param = iscsi_sw_tcp_conn_get_param,
.get_session_param = iscsi_session_get_param,
.start_conn = iscsi_conn_start,
- .stop_conn = iscsi_tcp_conn_stop,
+ .stop_conn = iscsi_sw_tcp_conn_stop,
/* iscsi host params */
.get_host_param = iscsi_host_get_param,
.set_host_param = iscsi_host_set_param,
/* IO */
.send_pdu = iscsi_conn_send_pdu,
- .get_stats = iscsi_conn_get_stats,
+ .get_stats = iscsi_sw_tcp_conn_get_stats,
+ /* iscsi task/cmd helpers */
.init_task = iscsi_tcp_task_init,
.xmit_task = iscsi_tcp_task_xmit,
.cleanup_task = iscsi_tcp_cleanup_task,
+ /* low level pdu helpers */
+ .xmit_pdu = iscsi_sw_tcp_pdu_xmit,
+ .init_pdu = iscsi_sw_tcp_pdu_init,
+ .alloc_pdu = iscsi_sw_tcp_pdu_alloc,
/* recovery */
.session_recovery_timedout = iscsi_session_recovery_timedout,
};
-static int __init
-iscsi_tcp_init(void)
+static int __init iscsi_sw_tcp_init(void)
{
if (iscsi_max_lun < 1) {
printk(KERN_ERR "iscsi_tcp: Invalid max_lun value of %u\n",
@@ -1984,19 +908,18 @@ iscsi_tcp_init(void)
return -EINVAL;
}
- iscsi_tcp_scsi_transport = iscsi_register_transport(
- &iscsi_tcp_transport);
- if (!iscsi_tcp_scsi_transport)
+ iscsi_sw_tcp_scsi_transport = iscsi_register_transport(
+ &iscsi_sw_tcp_transport);
+ if (!iscsi_sw_tcp_scsi_transport)
return -ENODEV;
return 0;
}
-static void __exit
-iscsi_tcp_exit(void)
+static void __exit iscsi_sw_tcp_exit(void)
{
- iscsi_unregister_transport(&iscsi_tcp_transport);
+ iscsi_unregister_transport(&iscsi_sw_tcp_transport);
}
-module_init(iscsi_tcp_init);
-module_exit(iscsi_tcp_exit);
+module_init(iscsi_sw_tcp_init);
+module_exit(iscsi_sw_tcp_exit);
diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h
index 498d8ca39848..ca6b7bc64de0 100644
--- a/drivers/scsi/iscsi_tcp.h
+++ b/drivers/scsi/iscsi_tcp.h
@@ -19,67 +19,27 @@
* See the file COPYING included with this distribution for more details.
*/
-#ifndef ISCSI_TCP_H
-#define ISCSI_TCP_H
+#ifndef ISCSI_SW_TCP_H
+#define ISCSI_SW_TCP_H
#include <scsi/libiscsi.h>
+#include <scsi/libiscsi_tcp.h>
-struct crypto_hash;
struct socket;
struct iscsi_tcp_conn;
-struct iscsi_segment;
-
-typedef int iscsi_segment_done_fn_t(struct iscsi_tcp_conn *,
- struct iscsi_segment *);
-
-struct iscsi_segment {
- unsigned char *data;
- unsigned int size;
- unsigned int copied;
- unsigned int total_size;
- unsigned int total_copied;
-
- struct hash_desc *hash;
- unsigned char recv_digest[ISCSI_DIGEST_SIZE];
- unsigned char digest[ISCSI_DIGEST_SIZE];
- unsigned int digest_len;
-
- struct scatterlist *sg;
- void *sg_mapped;
- unsigned int sg_offset;
-
- iscsi_segment_done_fn_t *done;
-};
-
-/* Socket connection recieve helper */
-struct iscsi_tcp_recv {
- struct iscsi_hdr *hdr;
- struct iscsi_segment segment;
-
- /* Allocate buffer for BHS + AHS */
- uint32_t hdr_buf[64];
-
- /* copied and flipped values */
- int datalen;
-};
/* Socket connection send helper */
-struct iscsi_tcp_send {
+struct iscsi_sw_tcp_send {
struct iscsi_hdr *hdr;
struct iscsi_segment segment;
struct iscsi_segment data_segment;
};
-struct iscsi_tcp_conn {
+struct iscsi_sw_tcp_conn {
struct iscsi_conn *iscsi_conn;
struct socket *sock;
- int stop_stage; /* conn_stop() flag: *
- * stop to recover, *
- * stop to terminate */
- /* control data */
- struct iscsi_tcp_recv in; /* TCP receive context */
- struct iscsi_tcp_send out; /* TCP send context */
+ struct iscsi_sw_tcp_send out;
/* old values for socket callbacks */
void (*old_data_ready)(struct sock *, int);
void (*old_state_change)(struct sock *);
@@ -93,41 +53,13 @@ struct iscsi_tcp_conn {
uint32_t sendpage_failures_cnt;
uint32_t discontiguous_hdr_cnt;
- int error;
-
ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int);
};
-struct iscsi_data_task {
- struct iscsi_data hdr; /* PDU */
- char hdrext[ISCSI_DIGEST_SIZE];/* Header-Digest */
-};
-
-struct iscsi_r2t_info {
- __be32 ttt; /* copied from R2T */
- __be32 exp_statsn; /* copied from R2T */
- uint32_t data_length; /* copied from R2T */
- uint32_t data_offset; /* copied from R2T */
- int sent; /* R2T sequence progress */
- int data_count; /* DATA-Out payload progress */
- int solicit_datasn;
- struct iscsi_data_task dtask; /* Data-Out header buf */
-};
-
-struct iscsi_tcp_task {
- struct iscsi_hdr_buff {
- struct iscsi_cmd cmd_hdr;
- char hdrextbuf[ISCSI_MAX_AHS_SIZE +
+struct iscsi_sw_tcp_hdrbuf {
+ struct iscsi_hdr hdrbuf;
+ char hdrextbuf[ISCSI_MAX_AHS_SIZE +
ISCSI_DIGEST_SIZE];
- } hdr;
-
- int sent;
- uint32_t exp_datasn; /* expected target's R2TSN/DataSN */
- int data_offset;
- struct iscsi_r2t_info *r2t; /* in progress R2T */
- struct iscsi_pool r2tpool;
- struct kfifo *r2tqueue;
- struct iscsi_data_task unsol_dtask; /* Data-Out header buf */
};
-#endif /* ISCSI_H */
+#endif /* ISCSI_SW_TCP_H */
diff --git a/drivers/scsi/libfc/Makefile b/drivers/scsi/libfc/Makefile
new file mode 100644
index 000000000000..55f982de3a9a
--- /dev/null
+++ b/drivers/scsi/libfc/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile
+
+obj-$(CONFIG_LIBFC) += libfc.o
+
+libfc-objs := \
+ fc_disc.o \
+ fc_exch.o \
+ fc_elsct.o \
+ fc_frame.o \
+ fc_lport.o \
+ fc_rport.o \
+ fc_fcp.o
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
new file mode 100644
index 000000000000..dd1564c9e04a
--- /dev/null
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -0,0 +1,845 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * Target Discovery
+ *
+ * This block discovers all FC-4 remote ports, including FCP initiators. It
+ * also handles RSCN events and re-discovery if necessary.
+ */
+
+/*
+ * DISC LOCKING
+ *
+ * The disc mutex is can be locked when acquiring rport locks, but may not
+ * be held when acquiring the lport lock. Refer to fc_lport.c for more
+ * details.
+ */
+
+#include <linux/timer.h>
+#include <linux/err.h>
+#include <asm/unaligned.h>
+
+#include <scsi/fc/fc_gs.h>
+
+#include <scsi/libfc.h>
+
+#define FC_DISC_RETRY_LIMIT 3 /* max retries */
+#define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */
+
+#define FC_DISC_DELAY 3
+
+static int fc_disc_debug;
+
+#define FC_DEBUG_DISC(fmt...) \
+ do { \
+ if (fc_disc_debug) \
+ FC_DBG(fmt); \
+ } while (0)
+
+static void fc_disc_gpn_ft_req(struct fc_disc *);
+static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
+static int fc_disc_new_target(struct fc_disc *, struct fc_rport *,
+ struct fc_rport_identifiers *);
+static void fc_disc_del_target(struct fc_disc *, struct fc_rport *);
+static void fc_disc_done(struct fc_disc *);
+static void fc_disc_timeout(struct work_struct *);
+static void fc_disc_single(struct fc_disc *, struct fc_disc_port *);
+static void fc_disc_restart(struct fc_disc *);
+
+/**
+ * fc_disc_lookup_rport - lookup a remote port by port_id
+ * @lport: Fibre Channel host port instance
+ * @port_id: remote port port_id to match
+ */
+struct fc_rport *fc_disc_lookup_rport(const struct fc_lport *lport,
+ u32 port_id)
+{
+ const struct fc_disc *disc = &lport->disc;
+ struct fc_rport *rport, *found = NULL;
+ struct fc_rport_libfc_priv *rdata;
+ int disc_found = 0;
+
+ list_for_each_entry(rdata, &disc->rports, peers) {
+ rport = PRIV_TO_RPORT(rdata);
+ if (rport->port_id == port_id) {
+ disc_found = 1;
+ found = rport;
+ break;
+ }
+ }
+
+ if (!disc_found)
+ found = NULL;
+
+ return found;
+}
+
+/**
+ * fc_disc_stop_rports - delete all the remote ports associated with the lport
+ * @disc: The discovery job to stop rports on
+ *
+ * Locking Note: This function expects that the lport mutex is locked before
+ * calling it.
+ */
+void fc_disc_stop_rports(struct fc_disc *disc)
+{
+ struct fc_lport *lport;
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rdata, *next;
+
+ lport = disc->lport;
+
+ mutex_lock(&disc->disc_mutex);
+ list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
+ rport = PRIV_TO_RPORT(rdata);
+ list_del(&rdata->peers);
+ lport->tt.rport_logoff(rport);
+ }
+
+ mutex_unlock(&disc->disc_mutex);
+}
+
+/**
+ * fc_disc_rport_callback - Event handler for rport events
+ * @lport: The lport which is receiving the event
+ * @rport: The rport which the event has occured on
+ * @event: The event that occured
+ *
+ * Locking Note: The rport lock should not be held when calling
+ * this function.
+ */
+static void fc_disc_rport_callback(struct fc_lport *lport,
+ struct fc_rport *rport,
+ enum fc_rport_event event)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_disc *disc = &lport->disc;
+ int found = 0;
+
+ FC_DEBUG_DISC("Received a %d event for port (%6x)\n", event,
+ rport->port_id);
+
+ if (event == RPORT_EV_CREATED) {
+ if (disc) {
+ found = 1;
+ mutex_lock(&disc->disc_mutex);
+ list_add_tail(&rdata->peers, &disc->rports);
+ mutex_unlock(&disc->disc_mutex);
+ }
+ }
+
+ if (!found)
+ FC_DEBUG_DISC("The rport (%6x) is not maintained "
+ "by the discovery layer\n", rport->port_id);
+}
+
+/**
+ * fc_disc_recv_rscn_req - Handle Registered State Change Notification (RSCN)
+ * @sp: Current sequence of the RSCN exchange
+ * @fp: RSCN Frame
+ * @lport: Fibre Channel host port instance
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_disc *disc)
+{
+ struct fc_lport *lport;
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rdata;
+ struct fc_els_rscn *rp;
+ struct fc_els_rscn_page *pp;
+ struct fc_seq_els_data rjt_data;
+ unsigned int len;
+ int redisc = 0;
+ enum fc_els_rscn_ev_qual ev_qual;
+ enum fc_els_rscn_addr_fmt fmt;
+ LIST_HEAD(disc_ports);
+ struct fc_disc_port *dp, *next;
+
+ lport = disc->lport;
+
+ FC_DEBUG_DISC("Received an RSCN event on port (%6x)\n",
+ fc_host_port_id(lport->host));
+
+ /* make sure the frame contains an RSCN message */
+ rp = fc_frame_payload_get(fp, sizeof(*rp));
+ if (!rp)
+ goto reject;
+ /* make sure the page length is as expected (4 bytes) */
+ if (rp->rscn_page_len != sizeof(*pp))
+ goto reject;
+ /* get the RSCN payload length */
+ len = ntohs(rp->rscn_plen);
+ if (len < sizeof(*rp))
+ goto reject;
+ /* make sure the frame contains the expected payload */
+ rp = fc_frame_payload_get(fp, len);
+ if (!rp)
+ goto reject;
+ /* payload must be a multiple of the RSCN page size */
+ len -= sizeof(*rp);
+ if (len % sizeof(*pp))
+ goto reject;
+
+ for (pp = (void *)(rp + 1); len > 0; len -= sizeof(*pp), pp++) {
+ ev_qual = pp->rscn_page_flags >> ELS_RSCN_EV_QUAL_BIT;
+ ev_qual &= ELS_RSCN_EV_QUAL_MASK;
+ fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT;
+ fmt &= ELS_RSCN_ADDR_FMT_MASK;
+ /*
+ * if we get an address format other than port
+ * (area, domain, fabric), then do a full discovery
+ */
+ switch (fmt) {
+ case ELS_ADDR_FMT_PORT:
+ FC_DEBUG_DISC("Port address format for port (%6x)\n",
+ ntoh24(pp->rscn_fid));
+ dp = kzalloc(sizeof(*dp), GFP_KERNEL);
+ if (!dp) {
+ redisc = 1;
+ break;
+ }
+ dp->lp = lport;
+ dp->ids.port_id = ntoh24(pp->rscn_fid);
+ dp->ids.port_name = -1;
+ dp->ids.node_name = -1;
+ dp->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ list_add_tail(&dp->peers, &disc_ports);
+ break;
+ case ELS_ADDR_FMT_AREA:
+ case ELS_ADDR_FMT_DOM:
+ case ELS_ADDR_FMT_FAB:
+ default:
+ FC_DEBUG_DISC("Address format is (%d)\n", fmt);
+ redisc = 1;
+ break;
+ }
+ }
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+ if (redisc) {
+ FC_DEBUG_DISC("RSCN received: rediscovering\n");
+ fc_disc_restart(disc);
+ } else {
+ FC_DEBUG_DISC("RSCN received: not rediscovering. "
+ "redisc %d state %d in_prog %d\n",
+ redisc, lport->state, disc->pending);
+ list_for_each_entry_safe(dp, next, &disc_ports, peers) {
+ list_del(&dp->peers);
+ rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
+ if (rport) {
+ rdata = RPORT_TO_PRIV(rport);
+ list_del(&rdata->peers);
+ lport->tt.rport_logoff(rport);
+ }
+ fc_disc_single(disc, dp);
+ }
+ }
+ fc_frame_free(fp);
+ return;
+reject:
+ FC_DEBUG_DISC("Received a bad RSCN frame\n");
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_LOGIC;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_disc_recv_req - Handle incoming requests
+ * @sp: Current sequence of the request exchange
+ * @fp: The frame
+ * @lport: The FC local port
+ *
+ * Locking Note: This function is called from the EM and will lock
+ * the disc_mutex before calling the handler for the
+ * request.
+ */
+static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_lport *lport)
+{
+ u8 op;
+ struct fc_disc *disc = &lport->disc;
+
+ op = fc_frame_payload_op(fp);
+ switch (op) {
+ case ELS_RSCN:
+ mutex_lock(&disc->disc_mutex);
+ fc_disc_recv_rscn_req(sp, fp, disc);
+ mutex_unlock(&disc->disc_mutex);
+ break;
+ default:
+ FC_DBG("Received an unsupported request. opcode (%x)\n", op);
+ break;
+ }
+}
+
+/**
+ * fc_disc_restart - Restart discovery
+ * @lport: FC discovery context
+ *
+ * Locking Note: This function expects that the disc mutex
+ * is already locked.
+ */
+static void fc_disc_restart(struct fc_disc *disc)
+{
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rdata, *next;
+ struct fc_lport *lport = disc->lport;
+
+ FC_DEBUG_DISC("Restarting discovery for port (%6x)\n",
+ fc_host_port_id(lport->host));
+
+ list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
+ rport = PRIV_TO_RPORT(rdata);
+ FC_DEBUG_DISC("list_del(%6x)\n", rport->port_id);
+ list_del(&rdata->peers);
+ lport->tt.rport_logoff(rport);
+ }
+
+ disc->requested = 1;
+ if (!disc->pending)
+ fc_disc_gpn_ft_req(disc);
+}
+
+/**
+ * fc_disc_start - Fibre Channel Target discovery
+ * @lport: FC local port
+ *
+ * Returns non-zero if discovery cannot be started.
+ */
+static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
+ enum fc_disc_event),
+ struct fc_lport *lport)
+{
+ struct fc_rport *rport;
+ struct fc_rport_identifiers ids;
+ struct fc_disc *disc = &lport->disc;
+
+ /*
+ * At this point we may have a new disc job or an existing
+ * one. Either way, let's lock when we make changes to it
+ * and send the GPN_FT request.
+ */
+ mutex_lock(&disc->disc_mutex);
+
+ disc->disc_callback = disc_callback;
+
+ /*
+ * If not ready, or already running discovery, just set request flag.
+ */
+ disc->requested = 1;
+
+ if (disc->pending) {
+ mutex_unlock(&disc->disc_mutex);
+ return;
+ }
+
+ /*
+ * Handle point-to-point mode as a simple discovery
+ * of the remote port. Yucky, yucky, yuck, yuck!
+ */
+ rport = disc->lport->ptp_rp;
+ if (rport) {
+ ids.port_id = rport->port_id;
+ ids.port_name = rport->port_name;
+ ids.node_name = rport->node_name;
+ ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ get_device(&rport->dev);
+
+ if (!fc_disc_new_target(disc, rport, &ids)) {
+ disc->event = DISC_EV_SUCCESS;
+ fc_disc_done(disc);
+ }
+ put_device(&rport->dev);
+ } else {
+ fc_disc_gpn_ft_req(disc); /* get ports by FC-4 type */
+ }
+
+ mutex_unlock(&disc->disc_mutex);
+}
+
+static struct fc_rport_operations fc_disc_rport_ops = {
+ .event_callback = fc_disc_rport_callback,
+};
+
+/**
+ * fc_disc_new_target - Handle new target found by discovery
+ * @lport: FC local port
+ * @rport: The previous FC remote port (NULL if new remote port)
+ * @ids: Identifiers for the new FC remote port
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static int fc_disc_new_target(struct fc_disc *disc,
+ struct fc_rport *rport,
+ struct fc_rport_identifiers *ids)
+{
+ struct fc_lport *lport = disc->lport;
+ struct fc_rport_libfc_priv *rp;
+ int error = 0;
+
+ if (rport && ids->port_name) {
+ if (rport->port_name == -1) {
+ /*
+ * Set WWN and fall through to notify of create.
+ */
+ fc_rport_set_name(rport, ids->port_name,
+ rport->node_name);
+ } else if (rport->port_name != ids->port_name) {
+ /*
+ * This is a new port with the same FCID as
+ * a previously-discovered port. Presumably the old
+ * port logged out and a new port logged in and was
+ * assigned the same FCID. This should be rare.
+ * Delete the old one and fall thru to re-create.
+ */
+ fc_disc_del_target(disc, rport);
+ rport = NULL;
+ }
+ }
+ if (((ids->port_name != -1) || (ids->port_id != -1)) &&
+ ids->port_id != fc_host_port_id(lport->host) &&
+ ids->port_name != lport->wwpn) {
+ if (!rport) {
+ rport = lport->tt.rport_lookup(lport, ids->port_id);
+ if (!rport) {
+ struct fc_disc_port dp;
+ dp.lp = lport;
+ dp.ids.port_id = ids->port_id;
+ dp.ids.port_name = ids->port_name;
+ dp.ids.node_name = ids->node_name;
+ dp.ids.roles = ids->roles;
+ rport = fc_rport_rogue_create(&dp);
+ }
+ if (!rport)
+ error = -ENOMEM;
+ }
+ if (rport) {
+ rp = rport->dd_data;
+ rp->ops = &fc_disc_rport_ops;
+ rp->rp_state = RPORT_ST_INIT;
+ lport->tt.rport_login(rport);
+ }
+ }
+ return error;
+}
+
+/**
+ * fc_disc_del_target - Delete a target
+ * @disc: FC discovery context
+ * @rport: The remote port to be removed
+ */
+static void fc_disc_del_target(struct fc_disc *disc, struct fc_rport *rport)
+{
+ struct fc_lport *lport = disc->lport;
+ struct fc_rport_libfc_priv *rdata = RPORT_TO_PRIV(rport);
+ list_del(&rdata->peers);
+ lport->tt.rport_logoff(rport);
+}
+
+/**
+ * fc_disc_done - Discovery has been completed
+ * @disc: FC discovery context
+ */
+static void fc_disc_done(struct fc_disc *disc)
+{
+ struct fc_lport *lport = disc->lport;
+
+ FC_DEBUG_DISC("Discovery complete for port (%6x)\n",
+ fc_host_port_id(lport->host));
+
+ disc->disc_callback(lport, disc->event);
+ disc->event = DISC_EV_NONE;
+
+ if (disc->requested)
+ fc_disc_gpn_ft_req(disc);
+ else
+ disc->pending = 0;
+}
+
+/**
+ * fc_disc_error - Handle error on dNS request
+ * @disc: FC discovery context
+ * @fp: The frame pointer
+ */
+static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
+{
+ struct fc_lport *lport = disc->lport;
+ unsigned long delay = 0;
+ if (fc_disc_debug)
+ FC_DBG("Error %ld, retries %d/%d\n",
+ PTR_ERR(fp), disc->retry_count,
+ FC_DISC_RETRY_LIMIT);
+
+ if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
+ /*
+ * Memory allocation failure, or the exchange timed out,
+ * retry after delay.
+ */
+ if (disc->retry_count < FC_DISC_RETRY_LIMIT) {
+ /* go ahead and retry */
+ if (!fp)
+ delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY);
+ else {
+ delay = msecs_to_jiffies(lport->e_d_tov);
+
+ /* timeout faster first time */
+ if (!disc->retry_count)
+ delay /= 4;
+ }
+ disc->retry_count++;
+ schedule_delayed_work(&disc->disc_work, delay);
+ } else {
+ /* exceeded retries */
+ disc->event = DISC_EV_FAILED;
+ fc_disc_done(disc);
+ }
+ }
+}
+
+/**
+ * fc_disc_gpn_ft_req - Send Get Port Names by FC-4 type (GPN_FT) request
+ * @lport: FC discovery context
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static void fc_disc_gpn_ft_req(struct fc_disc *disc)
+{
+ struct fc_frame *fp;
+ struct fc_lport *lport = disc->lport;
+
+ WARN_ON(!fc_lport_test_ready(lport));
+
+ disc->pending = 1;
+ disc->requested = 0;
+
+ disc->buf_len = 0;
+ disc->seq_count = 0;
+ fp = fc_frame_alloc(lport,
+ sizeof(struct fc_ct_hdr) +
+ sizeof(struct fc_ns_gid_ft));
+ if (!fp)
+ goto err;
+
+ if (lport->tt.elsct_send(lport, NULL, fp,
+ FC_NS_GPN_FT,
+ fc_disc_gpn_ft_resp,
+ disc, lport->e_d_tov))
+ return;
+err:
+ fc_disc_error(disc, fp);
+}
+
+/**
+ * fc_disc_gpn_ft_parse - Parse the list of IDs and names resulting from a request
+ * @lport: Fibre Channel host port instance
+ * @buf: GPN_FT response buffer
+ * @len: size of response buffer
+ */
+static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
+{
+ struct fc_lport *lport;
+ struct fc_gpn_ft_resp *np;
+ char *bp;
+ size_t plen;
+ size_t tlen;
+ int error = 0;
+ struct fc_disc_port dp;
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rdata;
+
+ lport = disc->lport;
+
+ /*
+ * Handle partial name record left over from previous call.
+ */
+ bp = buf;
+ plen = len;
+ np = (struct fc_gpn_ft_resp *)bp;
+ tlen = disc->buf_len;
+ if (tlen) {
+ WARN_ON(tlen >= sizeof(*np));
+ plen = sizeof(*np) - tlen;
+ WARN_ON(plen <= 0);
+ WARN_ON(plen >= sizeof(*np));
+ if (plen > len)
+ plen = len;
+ np = &disc->partial_buf;
+ memcpy((char *)np + tlen, bp, plen);
+
+ /*
+ * Set bp so that the loop below will advance it to the
+ * first valid full name element.
+ */
+ bp -= tlen;
+ len += tlen;
+ plen += tlen;
+ disc->buf_len = (unsigned char) plen;
+ if (plen == sizeof(*np))
+ disc->buf_len = 0;
+ }
+
+ /*
+ * Handle full name records, including the one filled from above.
+ * Normally, np == bp and plen == len, but from the partial case above,
+ * bp, len describe the overall buffer, and np, plen describe the
+ * partial buffer, which if would usually be full now.
+ * After the first time through the loop, things return to "normal".
+ */
+ while (plen >= sizeof(*np)) {
+ dp.lp = lport;
+ dp.ids.port_id = ntoh24(np->fp_fid);
+ dp.ids.port_name = ntohll(np->fp_wwpn);
+ dp.ids.node_name = -1;
+ dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
+ if ((dp.ids.port_id != fc_host_port_id(lport->host)) &&
+ (dp.ids.port_name != lport->wwpn)) {
+ rport = fc_rport_rogue_create(&dp);
+ if (rport) {
+ rdata = rport->dd_data;
+ rdata->ops = &fc_disc_rport_ops;
+ rdata->local_port = lport;
+ lport->tt.rport_login(rport);
+ } else
+ FC_DBG("Failed to allocate memory for "
+ "the newly discovered port (%6x)\n",
+ dp.ids.port_id);
+ }
+
+ if (np->fp_flags & FC_NS_FID_LAST) {
+ disc->event = DISC_EV_SUCCESS;
+ fc_disc_done(disc);
+ len = 0;
+ break;
+ }
+ len -= sizeof(*np);
+ bp += sizeof(*np);
+ np = (struct fc_gpn_ft_resp *)bp;
+ plen = len;
+ }
+
+ /*
+ * Save any partial record at the end of the buffer for next time.
+ */
+ if (error == 0 && len > 0 && len < sizeof(*np)) {
+ if (np != &disc->partial_buf) {
+ FC_DEBUG_DISC("Partial buffer remains "
+ "for discovery by (%6x)\n",
+ fc_host_port_id(lport->host));
+ memcpy(&disc->partial_buf, np, len);
+ }
+ disc->buf_len = (unsigned char) len;
+ } else {
+ disc->buf_len = 0;
+ }
+ return error;
+}
+
+/*
+ * Handle retry of memory allocation for remote ports.
+ */
+static void fc_disc_timeout(struct work_struct *work)
+{
+ struct fc_disc *disc = container_of(work,
+ struct fc_disc,
+ disc_work.work);
+ mutex_lock(&disc->disc_mutex);
+ if (disc->requested && !disc->pending)
+ fc_disc_gpn_ft_req(disc);
+ mutex_unlock(&disc->disc_mutex);
+}
+
+/**
+ * fc_disc_gpn_ft_resp - Handle a response frame from Get Port Names (GPN_FT)
+ * @sp: Current sequence of GPN_FT exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel host port instance
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *disc_arg)
+{
+ struct fc_disc *disc = disc_arg;
+ struct fc_ct_hdr *cp;
+ struct fc_frame_header *fh;
+ unsigned int seq_cnt;
+ void *buf = NULL;
+ unsigned int len;
+ int error;
+
+ FC_DEBUG_DISC("Received a GPN_FT response on port (%6x)\n",
+ fc_host_port_id(disc->lport->host));
+
+ if (IS_ERR(fp)) {
+ fc_disc_error(disc, fp);
+ return;
+ }
+
+ WARN_ON(!fc_frame_is_linear(fp)); /* buffer must be contiguous */
+ fh = fc_frame_header_get(fp);
+ len = fr_len(fp) - sizeof(*fh);
+ seq_cnt = ntohs(fh->fh_seq_cnt);
+ if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 &&
+ disc->seq_count == 0) {
+ cp = fc_frame_payload_get(fp, sizeof(*cp));
+ if (!cp) {
+ FC_DBG("GPN_FT response too short, len %d\n",
+ fr_len(fp));
+ } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
+
+ /*
+ * Accepted. Parse response.
+ */
+ buf = cp + 1;
+ len -= sizeof(*cp);
+ } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
+ FC_DBG("GPN_FT rejected reason %x exp %x "
+ "(check zoning)\n", cp->ct_reason,
+ cp->ct_explan);
+ disc->event = DISC_EV_FAILED;
+ fc_disc_done(disc);
+ } else {
+ FC_DBG("GPN_FT unexpected response code %x\n",
+ ntohs(cp->ct_cmd));
+ }
+ } else if (fr_sof(fp) == FC_SOF_N3 &&
+ seq_cnt == disc->seq_count) {
+ buf = fh + 1;
+ } else {
+ FC_DBG("GPN_FT unexpected frame - out of sequence? "
+ "seq_cnt %x expected %x sof %x eof %x\n",
+ seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
+ }
+ if (buf) {
+ error = fc_disc_gpn_ft_parse(disc, buf, len);
+ if (error)
+ fc_disc_error(disc, fp);
+ else
+ disc->seq_count++;
+ }
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_disc_single - Discover the directory information for a single target
+ * @lport: FC local port
+ * @dp: The port to rediscover
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
+{
+ struct fc_lport *lport;
+ struct fc_rport *rport;
+ struct fc_rport *new_rport;
+ struct fc_rport_libfc_priv *rdata;
+
+ lport = disc->lport;
+
+ if (dp->ids.port_id == fc_host_port_id(lport->host))
+ goto out;
+
+ rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
+ if (rport)
+ fc_disc_del_target(disc, rport);
+
+ new_rport = fc_rport_rogue_create(dp);
+ if (new_rport) {
+ rdata = new_rport->dd_data;
+ rdata->ops = &fc_disc_rport_ops;
+ kfree(dp);
+ lport->tt.rport_login(new_rport);
+ }
+ return;
+out:
+ kfree(dp);
+}
+
+/**
+ * fc_disc_stop - Stop discovery for a given lport
+ * @lport: The lport that discovery should stop for
+ */
+void fc_disc_stop(struct fc_lport *lport)
+{
+ struct fc_disc *disc = &lport->disc;
+
+ if (disc) {
+ cancel_delayed_work_sync(&disc->disc_work);
+ fc_disc_stop_rports(disc);
+ }
+}
+
+/**
+ * fc_disc_stop_final - Stop discovery for a given lport
+ * @lport: The lport that discovery should stop for
+ *
+ * This function will block until discovery has been
+ * completely stopped and all rports have been deleted.
+ */
+void fc_disc_stop_final(struct fc_lport *lport)
+{
+ fc_disc_stop(lport);
+ lport->tt.rport_flush_queue();
+}
+
+/**
+ * fc_disc_init - Initialize the discovery block
+ * @lport: FC local port
+ */
+int fc_disc_init(struct fc_lport *lport)
+{
+ struct fc_disc *disc;
+
+ if (!lport->tt.disc_start)
+ lport->tt.disc_start = fc_disc_start;
+
+ if (!lport->tt.disc_stop)
+ lport->tt.disc_stop = fc_disc_stop;
+
+ if (!lport->tt.disc_stop_final)
+ lport->tt.disc_stop_final = fc_disc_stop_final;
+
+ if (!lport->tt.disc_recv_req)
+ lport->tt.disc_recv_req = fc_disc_recv_req;
+
+ if (!lport->tt.rport_lookup)
+ lport->tt.rport_lookup = fc_disc_lookup_rport;
+
+ disc = &lport->disc;
+ INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
+ mutex_init(&disc->disc_mutex);
+ INIT_LIST_HEAD(&disc->rports);
+
+ disc->lport = lport;
+ disc->delay = FC_DISC_DELAY;
+ disc->event = DISC_EV_NONE;
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_disc_init);
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
new file mode 100644
index 000000000000..dd47fe619d1e
--- /dev/null
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ * 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * Provide interface to send ELS/CT FC frames
+ */
+
+#include <asm/unaligned.h>
+#include <scsi/fc/fc_gs.h>
+#include <scsi/fc/fc_ns.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+/*
+ * fc_elsct_send - sends ELS/CT frame
+ */
+static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
+ struct fc_rport *rport,
+ struct fc_frame *fp,
+ unsigned int op,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *fp,
+ void *arg),
+ void *arg, u32 timer_msec)
+{
+ enum fc_rctl r_ctl;
+ u32 did;
+ enum fc_fh_type fh_type;
+ int rc;
+
+ /* ELS requests */
+ if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS))
+ rc = fc_els_fill(lport, rport, fp, op, &r_ctl, &did, &fh_type);
+ else
+ /* CT requests */
+ rc = fc_ct_fill(lport, fp, op, &r_ctl, &did, &fh_type);
+
+ if (rc)
+ return NULL;
+
+ fc_fill_fc_hdr(fp, r_ctl, did, fc_host_port_id(lport->host), fh_type,
+ FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+ return lport->tt.exch_seq_send(lport, fp, resp, NULL, arg, timer_msec);
+}
+
+int fc_elsct_init(struct fc_lport *lport)
+{
+ if (!lport->tt.elsct_send)
+ lport->tt.elsct_send = fc_elsct_send;
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_elsct_init);
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
new file mode 100644
index 000000000000..66db08a5f27f
--- /dev/null
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -0,0 +1,1970 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 Red Hat, Inc. All rights reserved.
+ * Copyright(c) 2008 Mike Christie
+ *
+ * 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * Fibre Channel exchange and sequence handling.
+ */
+
+#include <linux/timer.h>
+#include <linux/gfp.h>
+#include <linux/err.h>
+
+#include <scsi/fc/fc_fc2.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+#define FC_DEF_R_A_TOV (10 * 1000) /* resource allocation timeout */
+
+/*
+ * fc_exch_debug can be set in debugger or at compile time to get more logs.
+ */
+static int fc_exch_debug;
+
+#define FC_DEBUG_EXCH(fmt...) \
+ do { \
+ if (fc_exch_debug) \
+ FC_DBG(fmt); \
+ } while (0)
+
+static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
+
+/*
+ * Structure and function definitions for managing Fibre Channel Exchanges
+ * and Sequences.
+ *
+ * The three primary structures used here are fc_exch_mgr, fc_exch, and fc_seq.
+ *
+ * fc_exch_mgr holds the exchange state for an N port
+ *
+ * fc_exch holds state for one exchange and links to its active sequence.
+ *
+ * fc_seq holds the state for an individual sequence.
+ */
+
+/*
+ * Exchange manager.
+ *
+ * This structure is the center for creating exchanges and sequences.
+ * It manages the allocation of exchange IDs.
+ */
+struct fc_exch_mgr {
+ enum fc_class class; /* default class for sequences */
+ spinlock_t em_lock; /* exchange manager lock,
+ must be taken before ex_lock */
+ u16 last_xid; /* last allocated exchange ID */
+ u16 min_xid; /* min exchange ID */
+ u16 max_xid; /* max exchange ID */
+ u16 max_read; /* max exchange ID for read */
+ u16 last_read; /* last xid allocated for read */
+ u32 total_exches; /* total allocated exchanges */
+ struct list_head ex_list; /* allocated exchanges list */
+ struct fc_lport *lp; /* fc device instance */
+ mempool_t *ep_pool; /* reserve ep's */
+
+ /*
+ * currently exchange mgr stats are updated but not used.
+ * either stats can be expose via sysfs or remove them
+ * all together if not used XXX
+ */
+ struct {
+ atomic_t no_free_exch;
+ atomic_t no_free_exch_xid;
+ atomic_t xid_not_found;
+ atomic_t xid_busy;
+ atomic_t seq_not_found;
+ atomic_t non_bls_resp;
+ } stats;
+ struct fc_exch **exches; /* for exch pointers indexed by xid */
+};
+#define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
+
+static void fc_exch_rrq(struct fc_exch *);
+static void fc_seq_ls_acc(struct fc_seq *);
+static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason,
+ enum fc_els_rjt_explan);
+static void fc_exch_els_rec(struct fc_seq *, struct fc_frame *);
+static void fc_exch_els_rrq(struct fc_seq *, struct fc_frame *);
+static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp);
+
+/*
+ * Internal implementation notes.
+ *
+ * The exchange manager is one by default in libfc but LLD may choose
+ * to have one per CPU. The sequence manager is one per exchange manager
+ * and currently never separated.
+ *
+ * Section 9.8 in FC-FS-2 specifies: "The SEQ_ID is a one-byte field
+ * assigned by the Sequence Initiator that shall be unique for a specific
+ * D_ID and S_ID pair while the Sequence is open." Note that it isn't
+ * qualified by exchange ID, which one might think it would be.
+ * In practice this limits the number of open sequences and exchanges to 256
+ * per session. For most targets we could treat this limit as per exchange.
+ *
+ * The exchange and its sequence are freed when the last sequence is received.
+ * It's possible for the remote port to leave an exchange open without
+ * sending any sequences.
+ *
+ * Notes on reference counts:
+ *
+ * Exchanges are reference counted and exchange gets freed when the reference
+ * count becomes zero.
+ *
+ * Timeouts:
+ * Sequences are timed out for E_D_TOV and R_A_TOV.
+ *
+ * Sequence event handling:
+ *
+ * The following events may occur on initiator sequences:
+ *
+ * Send.
+ * For now, the whole thing is sent.
+ * Receive ACK
+ * This applies only to class F.
+ * The sequence is marked complete.
+ * ULP completion.
+ * The upper layer calls fc_exch_done() when done
+ * with exchange and sequence tuple.
+ * RX-inferred completion.
+ * When we receive the next sequence on the same exchange, we can
+ * retire the previous sequence ID. (XXX not implemented).
+ * Timeout.
+ * R_A_TOV frees the sequence ID. If we're waiting for ACK,
+ * E_D_TOV causes abort and calls upper layer response handler
+ * with FC_EX_TIMEOUT error.
+ * Receive RJT
+ * XXX defer.
+ * Send ABTS
+ * On timeout.
+ *
+ * The following events may occur on recipient sequences:
+ *
+ * Receive
+ * Allocate sequence for first frame received.
+ * Hold during receive handler.
+ * Release when final frame received.
+ * Keep status of last N of these for the ELS RES command. XXX TBD.
+ * Receive ABTS
+ * Deallocate sequence
+ * Send RJT
+ * Deallocate
+ *
+ * For now, we neglect conditions where only part of a sequence was
+ * received or transmitted, or where out-of-order receipt is detected.
+ */
+
+/*
+ * Locking notes:
+ *
+ * The EM code run in a per-CPU worker thread.
+ *
+ * To protect against concurrency between a worker thread code and timers,
+ * sequence allocation and deallocation must be locked.
+ * - exchange refcnt can be done atomicly without locks.
+ * - sequence allocation must be locked by exch lock.
+ * - If the em_lock and ex_lock must be taken at the same time, then the
+ * em_lock must be taken before the ex_lock.
+ */
+
+/*
+ * opcode names for debugging.
+ */
+static char *fc_exch_rctl_names[] = FC_RCTL_NAMES_INIT;
+
+#define FC_TABLE_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+static inline const char *fc_exch_name_lookup(unsigned int op, char **table,
+ unsigned int max_index)
+{
+ const char *name = NULL;
+
+ if (op < max_index)
+ name = table[op];
+ if (!name)
+ name = "unknown";
+ return name;
+}
+
+static const char *fc_exch_rctl_name(unsigned int op)
+{
+ return fc_exch_name_lookup(op, fc_exch_rctl_names,
+ FC_TABLE_SIZE(fc_exch_rctl_names));
+}
+
+/*
+ * Hold an exchange - keep it from being freed.
+ */
+static void fc_exch_hold(struct fc_exch *ep)
+{
+ atomic_inc(&ep->ex_refcnt);
+}
+
+/*
+ * setup fc hdr by initializing few more FC header fields and sof/eof.
+ * Initialized fields by this func:
+ * - fh_ox_id, fh_rx_id, fh_seq_id, fh_seq_cnt
+ * - sof and eof
+ */
+static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
+ u32 f_ctl)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ u16 fill;
+
+ fr_sof(fp) = ep->class;
+ if (ep->seq.cnt)
+ fr_sof(fp) = fc_sof_normal(ep->class);
+
+ if (f_ctl & FC_FC_END_SEQ) {
+ fr_eof(fp) = FC_EOF_T;
+ if (fc_sof_needs_ack(ep->class))
+ fr_eof(fp) = FC_EOF_N;
+ /*
+ * Form f_ctl.
+ * The number of fill bytes to make the length a 4-byte
+ * multiple is the low order 2-bits of the f_ctl.
+ * The fill itself will have been cleared by the frame
+ * allocation.
+ * After this, the length will be even, as expected by
+ * the transport.
+ */
+ fill = fr_len(fp) & 3;
+ if (fill) {
+ fill = 4 - fill;
+ /* TODO, this may be a problem with fragmented skb */
+ skb_put(fp_skb(fp), fill);
+ hton24(fh->fh_f_ctl, f_ctl | fill);
+ }
+ } else {
+ WARN_ON(fr_len(fp) % 4 != 0); /* no pad to non last frame */
+ fr_eof(fp) = FC_EOF_N;
+ }
+
+ /*
+ * Initialize remainig fh fields
+ * from fc_fill_fc_hdr
+ */
+ fh->fh_ox_id = htons(ep->oxid);
+ fh->fh_rx_id = htons(ep->rxid);
+ fh->fh_seq_id = ep->seq.id;
+ fh->fh_seq_cnt = htons(ep->seq.cnt);
+}
+
+
+/*
+ * Release a reference to an exchange.
+ * If the refcnt goes to zero and the exchange is complete, it is freed.
+ */
+static void fc_exch_release(struct fc_exch *ep)
+{
+ struct fc_exch_mgr *mp;
+
+ if (atomic_dec_and_test(&ep->ex_refcnt)) {
+ mp = ep->em;
+ if (ep->destructor)
+ ep->destructor(&ep->seq, ep->arg);
+ if (ep->lp->tt.exch_put)
+ ep->lp->tt.exch_put(ep->lp, mp, ep->xid);
+ WARN_ON(!ep->esb_stat & ESB_ST_COMPLETE);
+ mempool_free(ep, mp->ep_pool);
+ }
+}
+
+static int fc_exch_done_locked(struct fc_exch *ep)
+{
+ int rc = 1;
+
+ /*
+ * We must check for completion in case there are two threads
+ * tyring to complete this. But the rrq code will reuse the
+ * ep, and in that case we only clear the resp and set it as
+ * complete, so it can be reused by the timer to send the rrq.
+ */
+ ep->resp = NULL;
+ if (ep->state & FC_EX_DONE)
+ return rc;
+ ep->esb_stat |= ESB_ST_COMPLETE;
+
+ if (!(ep->esb_stat & ESB_ST_REC_QUAL)) {
+ ep->state |= FC_EX_DONE;
+ if (cancel_delayed_work(&ep->timeout_work))
+ atomic_dec(&ep->ex_refcnt); /* drop hold for timer */
+ rc = 0;
+ }
+ return rc;
+}
+
+static void fc_exch_mgr_delete_ep(struct fc_exch *ep)
+{
+ struct fc_exch_mgr *mp;
+
+ mp = ep->em;
+ spin_lock_bh(&mp->em_lock);
+ WARN_ON(mp->total_exches <= 0);
+ mp->total_exches--;
+ mp->exches[ep->xid - mp->min_xid] = NULL;
+ list_del(&ep->ex_list);
+ spin_unlock_bh(&mp->em_lock);
+ fc_exch_release(ep); /* drop hold for exch in mp */
+}
+
+/*
+ * Internal version of fc_exch_timer_set - used with lock held.
+ */
+static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
+ unsigned int timer_msec)
+{
+ if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
+ return;
+
+ FC_DEBUG_EXCH("Exchange (%4x) timed out, notifying the upper layer\n",
+ ep->xid);
+ if (schedule_delayed_work(&ep->timeout_work,
+ msecs_to_jiffies(timer_msec)))
+ fc_exch_hold(ep); /* hold for timer */
+}
+
+/*
+ * Set timer for an exchange.
+ * The time is a minimum delay in milliseconds until the timer fires.
+ * Used for upper level protocols to time out the exchange.
+ * The timer is cancelled when it fires or when the exchange completes.
+ * Returns non-zero if a timer couldn't be allocated.
+ */
+static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
+{
+ spin_lock_bh(&ep->ex_lock);
+ fc_exch_timer_set_locked(ep, timer_msec);
+ spin_unlock_bh(&ep->ex_lock);
+}
+
+int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
+{
+ struct fc_seq *sp;
+ struct fc_exch *ep;
+ struct fc_frame *fp;
+ int error;
+
+ ep = fc_seq_exch(req_sp);
+
+ spin_lock_bh(&ep->ex_lock);
+ if (ep->esb_stat & (ESB_ST_COMPLETE | ESB_ST_ABNORMAL) ||
+ ep->state & (FC_EX_DONE | FC_EX_RST_CLEANUP)) {
+ spin_unlock_bh(&ep->ex_lock);
+ return -ENXIO;
+ }
+
+ /*
+ * Send the abort on a new sequence if possible.
+ */
+ sp = fc_seq_start_next_locked(&ep->seq);
+ if (!sp) {
+ spin_unlock_bh(&ep->ex_lock);
+ return -ENOMEM;
+ }
+
+ ep->esb_stat |= ESB_ST_SEQ_INIT | ESB_ST_ABNORMAL;
+ if (timer_msec)
+ fc_exch_timer_set_locked(ep, timer_msec);
+ spin_unlock_bh(&ep->ex_lock);
+
+ /*
+ * If not logged into the fabric, don't send ABTS but leave
+ * sequence active until next timeout.
+ */
+ if (!ep->sid)
+ return 0;
+
+ /*
+ * Send an abort for the sequence that timed out.
+ */
+ fp = fc_frame_alloc(ep->lp, 0);
+ if (fp) {
+ fc_fill_fc_hdr(fp, FC_RCTL_BA_ABTS, ep->did, ep->sid,
+ FC_TYPE_BLS, FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+ error = fc_seq_send(ep->lp, sp, fp);
+ } else
+ error = -ENOBUFS;
+ return error;
+}
+EXPORT_SYMBOL(fc_seq_exch_abort);
+
+/*
+ * Exchange timeout - handle exchange timer expiration.
+ * The timer will have been cancelled before this is called.
+ */
+static void fc_exch_timeout(struct work_struct *work)
+{
+ struct fc_exch *ep = container_of(work, struct fc_exch,
+ timeout_work.work);
+ struct fc_seq *sp = &ep->seq;
+ void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
+ void *arg;
+ u32 e_stat;
+ int rc = 1;
+
+ spin_lock_bh(&ep->ex_lock);
+ if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
+ goto unlock;
+
+ e_stat = ep->esb_stat;
+ if (e_stat & ESB_ST_COMPLETE) {
+ ep->esb_stat = e_stat & ~ESB_ST_REC_QUAL;
+ if (e_stat & ESB_ST_REC_QUAL)
+ fc_exch_rrq(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ goto done;
+ } else {
+ resp = ep->resp;
+ arg = ep->arg;
+ ep->resp = NULL;
+ if (e_stat & ESB_ST_ABNORMAL)
+ rc = fc_exch_done_locked(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_mgr_delete_ep(ep);
+ if (resp)
+ resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg);
+ fc_seq_exch_abort(sp, 2 * ep->r_a_tov);
+ goto done;
+ }
+unlock:
+ spin_unlock_bh(&ep->ex_lock);
+done:
+ /*
+ * This release matches the hold taken when the timer was set.
+ */
+ fc_exch_release(ep);
+}
+
+/*
+ * Allocate a sequence.
+ *
+ * We don't support multiple originated sequences on the same exchange.
+ * By implication, any previously originated sequence on this exchange
+ * is complete, and we reallocate the same sequence.
+ */
+static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id)
+{
+ struct fc_seq *sp;
+
+ sp = &ep->seq;
+ sp->ssb_stat = 0;
+ sp->cnt = 0;
+ sp->id = seq_id;
+ return sp;
+}
+
+/*
+ * fc_em_alloc_xid - returns an xid based on request type
+ * @lp : ptr to associated lport
+ * @fp : ptr to the assocated frame
+ *
+ * check the associated fc_fsp_pkt to get scsi command type and
+ * command direction to decide from which range this exch id
+ * will be allocated from.
+ *
+ * Returns : 0 or an valid xid
+ */
+static u16 fc_em_alloc_xid(struct fc_exch_mgr *mp, const struct fc_frame *fp)
+{
+ u16 xid, min, max;
+ u16 *plast;
+ struct fc_exch *ep = NULL;
+
+ if (mp->max_read) {
+ if (fc_frame_is_read(fp)) {
+ min = mp->min_xid;
+ max = mp->max_read;
+ plast = &mp->last_read;
+ } else {
+ min = mp->max_read + 1;
+ max = mp->max_xid;
+ plast = &mp->last_xid;
+ }
+ } else {
+ min = mp->min_xid;
+ max = mp->max_xid;
+ plast = &mp->last_xid;
+ }
+ xid = *plast;
+ do {
+ xid = (xid == max) ? min : xid + 1;
+ ep = mp->exches[xid - mp->min_xid];
+ } while ((ep != NULL) && (xid != *plast));
+
+ if (unlikely(ep))
+ xid = 0;
+ else
+ *plast = xid;
+
+ return xid;
+}
+
+/*
+ * fc_exch_alloc - allocate an exchange.
+ * @mp : ptr to the exchange manager
+ * @xid: input xid
+ *
+ * if xid is supplied zero then assign next free exchange ID
+ * from exchange manager, otherwise use supplied xid.
+ * Returns with exch lock held.
+ */
+struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
+ struct fc_frame *fp, u16 xid)
+{
+ struct fc_exch *ep;
+
+ /* allocate memory for exchange */
+ ep = mempool_alloc(mp->ep_pool, GFP_ATOMIC);
+ if (!ep) {
+ atomic_inc(&mp->stats.no_free_exch);
+ goto out;
+ }
+ memset(ep, 0, sizeof(*ep));
+
+ spin_lock_bh(&mp->em_lock);
+ /* alloc xid if input xid 0 */
+ if (!xid) {
+ /* alloc a new xid */
+ xid = fc_em_alloc_xid(mp, fp);
+ if (!xid) {
+ printk(KERN_ERR "fc_em_alloc_xid() failed\n");
+ goto err;
+ }
+ }
+
+ fc_exch_hold(ep); /* hold for exch in mp */
+ spin_lock_init(&ep->ex_lock);
+ /*
+ * Hold exch lock for caller to prevent fc_exch_reset()
+ * from releasing exch while fc_exch_alloc() caller is
+ * still working on exch.
+ */
+ spin_lock_bh(&ep->ex_lock);
+
+ mp->exches[xid - mp->min_xid] = ep;
+ list_add_tail(&ep->ex_list, &mp->ex_list);
+ fc_seq_alloc(ep, ep->seq_id++);
+ mp->total_exches++;
+ spin_unlock_bh(&mp->em_lock);
+
+ /*
+ * update exchange
+ */
+ ep->oxid = ep->xid = xid;
+ ep->em = mp;
+ ep->lp = mp->lp;
+ ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */
+ ep->rxid = FC_XID_UNKNOWN;
+ ep->class = mp->class;
+ INIT_DELAYED_WORK(&ep->timeout_work, fc_exch_timeout);
+out:
+ return ep;
+err:
+ spin_unlock_bh(&mp->em_lock);
+ atomic_inc(&mp->stats.no_free_exch_xid);
+ mempool_free(ep, mp->ep_pool);
+ return NULL;
+}
+EXPORT_SYMBOL(fc_exch_alloc);
+
+/*
+ * Lookup and hold an exchange.
+ */
+static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
+{
+ struct fc_exch *ep = NULL;
+
+ if ((xid >= mp->min_xid) && (xid <= mp->max_xid)) {
+ spin_lock_bh(&mp->em_lock);
+ ep = mp->exches[xid - mp->min_xid];
+ if (ep) {
+ fc_exch_hold(ep);
+ WARN_ON(ep->xid != xid);
+ }
+ spin_unlock_bh(&mp->em_lock);
+ }
+ return ep;
+}
+
+void fc_exch_done(struct fc_seq *sp)
+{
+ struct fc_exch *ep = fc_seq_exch(sp);
+ int rc;
+
+ spin_lock_bh(&ep->ex_lock);
+ rc = fc_exch_done_locked(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_mgr_delete_ep(ep);
+}
+EXPORT_SYMBOL(fc_exch_done);
+
+/*
+ * Allocate a new exchange as responder.
+ * Sets the responder ID in the frame header.
+ */
+static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
+{
+ struct fc_exch *ep;
+ struct fc_frame_header *fh;
+ u16 rxid;
+
+ ep = mp->lp->tt.exch_get(mp->lp, fp);
+ if (ep) {
+ ep->class = fc_frame_class(fp);
+
+ /*
+ * Set EX_CTX indicating we're responding on this exchange.
+ */
+ ep->f_ctl |= FC_FC_EX_CTX; /* we're responding */
+ ep->f_ctl &= ~FC_FC_FIRST_SEQ; /* not new */
+ fh = fc_frame_header_get(fp);
+ ep->sid = ntoh24(fh->fh_d_id);
+ ep->did = ntoh24(fh->fh_s_id);
+ ep->oid = ep->did;
+
+ /*
+ * Allocated exchange has placed the XID in the
+ * originator field. Move it to the responder field,
+ * and set the originator XID from the frame.
+ */
+ ep->rxid = ep->xid;
+ ep->oxid = ntohs(fh->fh_ox_id);
+ ep->esb_stat |= ESB_ST_RESP | ESB_ST_SEQ_INIT;
+ if ((ntoh24(fh->fh_f_ctl) & FC_FC_SEQ_INIT) == 0)
+ ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+
+ /*
+ * Set the responder ID in the frame header.
+ * The old one should've been 0xffff.
+ * If it isn't, don't assign one.
+ * Incoming basic link service frames may specify
+ * a referenced RX_ID.
+ */
+ if (fh->fh_type != FC_TYPE_BLS) {
+ rxid = ntohs(fh->fh_rx_id);
+ WARN_ON(rxid != FC_XID_UNKNOWN);
+ fh->fh_rx_id = htons(ep->rxid);
+ }
+ fc_exch_hold(ep); /* hold for caller */
+ spin_unlock_bh(&ep->ex_lock); /* lock from exch_get */
+ }
+ return ep;
+}
+
+/*
+ * Find a sequence for receive where the other end is originating the sequence.
+ * If fc_pf_rjt_reason is FC_RJT_NONE then this function will have a hold
+ * on the ep that should be released by the caller.
+ */
+static enum fc_pf_rjt_reason
+fc_seq_lookup_recip(struct fc_exch_mgr *mp, struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fc_exch *ep = NULL;
+ struct fc_seq *sp = NULL;
+ enum fc_pf_rjt_reason reject = FC_RJT_NONE;
+ u32 f_ctl;
+ u16 xid;
+
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ WARN_ON((f_ctl & FC_FC_SEQ_CTX) != 0);
+
+ /*
+ * Lookup or create the exchange if we will be creating the sequence.
+ */
+ if (f_ctl & FC_FC_EX_CTX) {
+ xid = ntohs(fh->fh_ox_id); /* we originated exch */
+ ep = fc_exch_find(mp, xid);
+ if (!ep) {
+ atomic_inc(&mp->stats.xid_not_found);
+ reject = FC_RJT_OX_ID;
+ goto out;
+ }
+ if (ep->rxid == FC_XID_UNKNOWN)
+ ep->rxid = ntohs(fh->fh_rx_id);
+ else if (ep->rxid != ntohs(fh->fh_rx_id)) {
+ reject = FC_RJT_OX_ID;
+ goto rel;
+ }
+ } else {
+ xid = ntohs(fh->fh_rx_id); /* we are the responder */
+
+ /*
+ * Special case for MDS issuing an ELS TEST with a
+ * bad rxid of 0.
+ * XXX take this out once we do the proper reject.
+ */
+ if (xid == 0 && fh->fh_r_ctl == FC_RCTL_ELS_REQ &&
+ fc_frame_payload_op(fp) == ELS_TEST) {
+ fh->fh_rx_id = htons(FC_XID_UNKNOWN);
+ xid = FC_XID_UNKNOWN;
+ }
+
+ /*
+ * new sequence - find the exchange
+ */
+ ep = fc_exch_find(mp, xid);
+ if ((f_ctl & FC_FC_FIRST_SEQ) && fc_sof_is_init(fr_sof(fp))) {
+ if (ep) {
+ atomic_inc(&mp->stats.xid_busy);
+ reject = FC_RJT_RX_ID;
+ goto rel;
+ }
+ ep = fc_exch_resp(mp, fp);
+ if (!ep) {
+ reject = FC_RJT_EXCH_EST; /* XXX */
+ goto out;
+ }
+ xid = ep->xid; /* get our XID */
+ } else if (!ep) {
+ atomic_inc(&mp->stats.xid_not_found);
+ reject = FC_RJT_RX_ID; /* XID not found */
+ goto out;
+ }
+ }
+
+ /*
+ * At this point, we have the exchange held.
+ * Find or create the sequence.
+ */
+ if (fc_sof_is_init(fr_sof(fp))) {
+ sp = fc_seq_start_next(&ep->seq);
+ if (!sp) {
+ reject = FC_RJT_SEQ_XS; /* exchange shortage */
+ goto rel;
+ }
+ sp->id = fh->fh_seq_id;
+ sp->ssb_stat |= SSB_ST_RESP;
+ } else {
+ sp = &ep->seq;
+ if (sp->id != fh->fh_seq_id) {
+ atomic_inc(&mp->stats.seq_not_found);
+ reject = FC_RJT_SEQ_ID; /* sequence/exch should exist */
+ goto rel;
+ }
+ }
+ WARN_ON(ep != fc_seq_exch(sp));
+
+ if (f_ctl & FC_FC_SEQ_INIT)
+ ep->esb_stat |= ESB_ST_SEQ_INIT;
+
+ fr_seq(fp) = sp;
+out:
+ return reject;
+rel:
+ fc_exch_done(&ep->seq);
+ fc_exch_release(ep); /* hold from fc_exch_find/fc_exch_resp */
+ return reject;
+}
+
+/*
+ * Find the sequence for a frame being received.
+ * We originated the sequence, so it should be found.
+ * We may or may not have originated the exchange.
+ * Does not hold the sequence for the caller.
+ */
+static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fc_exch *ep;
+ struct fc_seq *sp = NULL;
+ u32 f_ctl;
+ u16 xid;
+
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ WARN_ON((f_ctl & FC_FC_SEQ_CTX) != FC_FC_SEQ_CTX);
+ xid = ntohs((f_ctl & FC_FC_EX_CTX) ? fh->fh_ox_id : fh->fh_rx_id);
+ ep = fc_exch_find(mp, xid);
+ if (!ep)
+ return NULL;
+ if (ep->seq.id == fh->fh_seq_id) {
+ /*
+ * Save the RX_ID if we didn't previously know it.
+ */
+ sp = &ep->seq;
+ if ((f_ctl & FC_FC_EX_CTX) != 0 &&
+ ep->rxid == FC_XID_UNKNOWN) {
+ ep->rxid = ntohs(fh->fh_rx_id);
+ }
+ }
+ fc_exch_release(ep);
+ return sp;
+}
+
+/*
+ * Set addresses for an exchange.
+ * Note this must be done before the first sequence of the exchange is sent.
+ */
+static void fc_exch_set_addr(struct fc_exch *ep,
+ u32 orig_id, u32 resp_id)
+{
+ ep->oid = orig_id;
+ if (ep->esb_stat & ESB_ST_RESP) {
+ ep->sid = resp_id;
+ ep->did = orig_id;
+ } else {
+ ep->sid = orig_id;
+ ep->did = resp_id;
+ }
+}
+
+static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp)
+{
+ struct fc_exch *ep = fc_seq_exch(sp);
+
+ sp = fc_seq_alloc(ep, ep->seq_id++);
+ FC_DEBUG_EXCH("exch %4x f_ctl %6x seq %2x\n",
+ ep->xid, ep->f_ctl, sp->id);
+ return sp;
+}
+/*
+ * Allocate a new sequence on the same exchange as the supplied sequence.
+ * This will never return NULL.
+ */
+struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
+{
+ struct fc_exch *ep = fc_seq_exch(sp);
+
+ spin_lock_bh(&ep->ex_lock);
+ WARN_ON((ep->esb_stat & ESB_ST_COMPLETE) != 0);
+ sp = fc_seq_start_next_locked(sp);
+ spin_unlock_bh(&ep->ex_lock);
+
+ return sp;
+}
+EXPORT_SYMBOL(fc_seq_start_next);
+
+int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp)
+{
+ struct fc_exch *ep;
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ int error;
+ u32 f_ctl;
+
+ ep = fc_seq_exch(sp);
+ WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT);
+
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ fc_exch_setup_hdr(ep, fp, f_ctl);
+
+ /*
+ * update sequence count if this frame is carrying
+ * multiple FC frames when sequence offload is enabled
+ * by LLD.
+ */
+ if (fr_max_payload(fp))
+ sp->cnt += DIV_ROUND_UP((fr_len(fp) - sizeof(*fh)),
+ fr_max_payload(fp));
+ else
+ sp->cnt++;
+
+ /*
+ * Send the frame.
+ */
+ error = lp->tt.frame_send(lp, fp);
+
+ /*
+ * Update the exchange and sequence flags,
+ * assuming all frames for the sequence have been sent.
+ * We can only be called to send once for each sequence.
+ */
+ spin_lock_bh(&ep->ex_lock);
+ ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ; /* not first seq */
+ if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT))
+ ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+ spin_unlock_bh(&ep->ex_lock);
+ return error;
+}
+EXPORT_SYMBOL(fc_seq_send);
+
+void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
+ struct fc_seq_els_data *els_data)
+{
+ switch (els_cmd) {
+ case ELS_LS_RJT:
+ fc_seq_ls_rjt(sp, els_data->reason, els_data->explan);
+ break;
+ case ELS_LS_ACC:
+ fc_seq_ls_acc(sp);
+ break;
+ case ELS_RRQ:
+ fc_exch_els_rrq(sp, els_data->fp);
+ break;
+ case ELS_REC:
+ fc_exch_els_rec(sp, els_data->fp);
+ break;
+ default:
+ FC_DBG("Invalid ELS CMD:%x\n", els_cmd);
+ }
+}
+EXPORT_SYMBOL(fc_seq_els_rsp_send);
+
+/*
+ * Send a sequence, which is also the last sequence in the exchange.
+ */
+static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,
+ enum fc_rctl rctl, enum fc_fh_type fh_type)
+{
+ u32 f_ctl;
+ struct fc_exch *ep = fc_seq_exch(sp);
+
+ f_ctl = FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ f_ctl |= ep->f_ctl;
+ fc_fill_fc_hdr(fp, rctl, ep->did, ep->sid, fh_type, f_ctl, 0);
+ fc_seq_send(ep->lp, sp, fp);
+}
+
+/*
+ * Send ACK_1 (or equiv.) indicating we received something.
+ * The frame we're acking is supplied.
+ */
+static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
+{
+ struct fc_frame *fp;
+ struct fc_frame_header *rx_fh;
+ struct fc_frame_header *fh;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ struct fc_lport *lp = ep->lp;
+ unsigned int f_ctl;
+
+ /*
+ * Don't send ACKs for class 3.
+ */
+ if (fc_sof_needs_ack(fr_sof(rx_fp))) {
+ fp = fc_frame_alloc(lp, 0);
+ if (!fp)
+ return;
+
+ fh = fc_frame_header_get(fp);
+ fh->fh_r_ctl = FC_RCTL_ACK_1;
+ fh->fh_type = FC_TYPE_BLS;
+
+ /*
+ * Form f_ctl by inverting EX_CTX and SEQ_CTX (bits 23, 22).
+ * Echo FIRST_SEQ, LAST_SEQ, END_SEQ, END_CONN, SEQ_INIT.
+ * Bits 9-8 are meaningful (retransmitted or unidirectional).
+ * Last ACK uses bits 7-6 (continue sequence),
+ * bits 5-4 are meaningful (what kind of ACK to use).
+ */
+ rx_fh = fc_frame_header_get(rx_fp);
+ f_ctl = ntoh24(rx_fh->fh_f_ctl);
+ f_ctl &= FC_FC_EX_CTX | FC_FC_SEQ_CTX |
+ FC_FC_FIRST_SEQ | FC_FC_LAST_SEQ |
+ FC_FC_END_SEQ | FC_FC_END_CONN | FC_FC_SEQ_INIT |
+ FC_FC_RETX_SEQ | FC_FC_UNI_TX;
+ f_ctl ^= FC_FC_EX_CTX | FC_FC_SEQ_CTX;
+ hton24(fh->fh_f_ctl, f_ctl);
+
+ fc_exch_setup_hdr(ep, fp, f_ctl);
+ fh->fh_seq_id = rx_fh->fh_seq_id;
+ fh->fh_seq_cnt = rx_fh->fh_seq_cnt;
+ fh->fh_parm_offset = htonl(1); /* ack single frame */
+
+ fr_sof(fp) = fr_sof(rx_fp);
+ if (f_ctl & FC_FC_END_SEQ)
+ fr_eof(fp) = FC_EOF_T;
+ else
+ fr_eof(fp) = FC_EOF_N;
+
+ (void) lp->tt.frame_send(lp, fp);
+ }
+}
+
+/*
+ * Send BLS Reject.
+ * This is for rejecting BA_ABTS only.
+ */
+static void
+fc_exch_send_ba_rjt(struct fc_frame *rx_fp, enum fc_ba_rjt_reason reason,
+ enum fc_ba_rjt_explan explan)
+{
+ struct fc_frame *fp;
+ struct fc_frame_header *rx_fh;
+ struct fc_frame_header *fh;
+ struct fc_ba_rjt *rp;
+ struct fc_lport *lp;
+ unsigned int f_ctl;
+
+ lp = fr_dev(rx_fp);
+ fp = fc_frame_alloc(lp, sizeof(*rp));
+ if (!fp)
+ return;
+ fh = fc_frame_header_get(fp);
+ rx_fh = fc_frame_header_get(rx_fp);
+
+ memset(fh, 0, sizeof(*fh) + sizeof(*rp));
+
+ rp = fc_frame_payload_get(fp, sizeof(*rp));
+ rp->br_reason = reason;
+ rp->br_explan = explan;
+
+ /*
+ * seq_id, cs_ctl, df_ctl and param/offset are zero.
+ */
+ memcpy(fh->fh_s_id, rx_fh->fh_d_id, 3);
+ memcpy(fh->fh_d_id, rx_fh->fh_s_id, 3);
+ fh->fh_ox_id = rx_fh->fh_rx_id;
+ fh->fh_rx_id = rx_fh->fh_ox_id;
+ fh->fh_seq_cnt = rx_fh->fh_seq_cnt;
+ fh->fh_r_ctl = FC_RCTL_BA_RJT;
+ fh->fh_type = FC_TYPE_BLS;
+
+ /*
+ * Form f_ctl by inverting EX_CTX and SEQ_CTX (bits 23, 22).
+ * Echo FIRST_SEQ, LAST_SEQ, END_SEQ, END_CONN, SEQ_INIT.
+ * Bits 9-8 are meaningful (retransmitted or unidirectional).
+ * Last ACK uses bits 7-6 (continue sequence),
+ * bits 5-4 are meaningful (what kind of ACK to use).
+ * Always set LAST_SEQ, END_SEQ.
+ */
+ f_ctl = ntoh24(rx_fh->fh_f_ctl);
+ f_ctl &= FC_FC_EX_CTX | FC_FC_SEQ_CTX |
+ FC_FC_END_CONN | FC_FC_SEQ_INIT |
+ FC_FC_RETX_SEQ | FC_FC_UNI_TX;
+ f_ctl ^= FC_FC_EX_CTX | FC_FC_SEQ_CTX;
+ f_ctl |= FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+ f_ctl &= ~FC_FC_FIRST_SEQ;
+ hton24(fh->fh_f_ctl, f_ctl);
+
+ fr_sof(fp) = fc_sof_class(fr_sof(rx_fp));
+ fr_eof(fp) = FC_EOF_T;
+ if (fc_sof_needs_ack(fr_sof(fp)))
+ fr_eof(fp) = FC_EOF_N;
+
+ (void) lp->tt.frame_send(lp, fp);
+}
+
+/*
+ * Handle an incoming ABTS. This would be for target mode usually,
+ * but could be due to lost FCP transfer ready, confirm or RRQ.
+ * We always handle this as an exchange abort, ignoring the parameter.
+ */
+static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp)
+{
+ struct fc_frame *fp;
+ struct fc_ba_acc *ap;
+ struct fc_frame_header *fh;
+ struct fc_seq *sp;
+
+ if (!ep)
+ goto reject;
+ spin_lock_bh(&ep->ex_lock);
+ if (ep->esb_stat & ESB_ST_COMPLETE) {
+ spin_unlock_bh(&ep->ex_lock);
+ goto reject;
+ }
+ if (!(ep->esb_stat & ESB_ST_REC_QUAL))
+ fc_exch_hold(ep); /* hold for REC_QUAL */
+ ep->esb_stat |= ESB_ST_ABNORMAL | ESB_ST_REC_QUAL;
+ fc_exch_timer_set_locked(ep, ep->r_a_tov);
+
+ fp = fc_frame_alloc(ep->lp, sizeof(*ap));
+ if (!fp) {
+ spin_unlock_bh(&ep->ex_lock);
+ goto free;
+ }
+ fh = fc_frame_header_get(fp);
+ ap = fc_frame_payload_get(fp, sizeof(*ap));
+ memset(ap, 0, sizeof(*ap));
+ sp = &ep->seq;
+ ap->ba_high_seq_cnt = htons(0xffff);
+ if (sp->ssb_stat & SSB_ST_RESP) {
+ ap->ba_seq_id = sp->id;
+ ap->ba_seq_id_val = FC_BA_SEQ_ID_VAL;
+ ap->ba_high_seq_cnt = fh->fh_seq_cnt;
+ ap->ba_low_seq_cnt = htons(sp->cnt);
+ }
+ sp = fc_seq_start_next(sp);
+ spin_unlock_bh(&ep->ex_lock);
+ fc_seq_send_last(sp, fp, FC_RCTL_BA_ACC, FC_TYPE_BLS);
+ fc_frame_free(rx_fp);
+ return;
+
+reject:
+ fc_exch_send_ba_rjt(rx_fp, FC_BA_RJT_UNABLE, FC_BA_RJT_INV_XID);
+free:
+ fc_frame_free(rx_fp);
+}
+
+/*
+ * Handle receive where the other end is originating the sequence.
+ */
+static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fc_seq *sp = NULL;
+ struct fc_exch *ep = NULL;
+ enum fc_sof sof;
+ enum fc_eof eof;
+ u32 f_ctl;
+ enum fc_pf_rjt_reason reject;
+
+ fr_seq(fp) = NULL;
+ reject = fc_seq_lookup_recip(mp, fp);
+ if (reject == FC_RJT_NONE) {
+ sp = fr_seq(fp); /* sequence will be held */
+ ep = fc_seq_exch(sp);
+ sof = fr_sof(fp);
+ eof = fr_eof(fp);
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ fc_seq_send_ack(sp, fp);
+
+ /*
+ * Call the receive function.
+ *
+ * The receive function may allocate a new sequence
+ * over the old one, so we shouldn't change the
+ * sequence after this.
+ *
+ * The frame will be freed by the receive function.
+ * If new exch resp handler is valid then call that
+ * first.
+ */
+ if (ep->resp)
+ ep->resp(sp, fp, ep->arg);
+ else
+ lp->tt.lport_recv(lp, sp, fp);
+ fc_exch_release(ep); /* release from lookup */
+ } else {
+ FC_DEBUG_EXCH("exch/seq lookup failed: reject %x\n", reject);
+ fc_frame_free(fp);
+ }
+}
+
+/*
+ * Handle receive where the other end is originating the sequence in
+ * response to our exchange.
+ */
+static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fc_seq *sp;
+ struct fc_exch *ep;
+ enum fc_sof sof;
+ u32 f_ctl;
+ void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
+ void *ex_resp_arg;
+ int rc;
+
+ ep = fc_exch_find(mp, ntohs(fh->fh_ox_id));
+ if (!ep) {
+ atomic_inc(&mp->stats.xid_not_found);
+ goto out;
+ }
+ if (ep->rxid == FC_XID_UNKNOWN)
+ ep->rxid = ntohs(fh->fh_rx_id);
+ if (ep->sid != 0 && ep->sid != ntoh24(fh->fh_d_id)) {
+ atomic_inc(&mp->stats.xid_not_found);
+ goto rel;
+ }
+ if (ep->did != ntoh24(fh->fh_s_id) &&
+ ep->did != FC_FID_FLOGI) {
+ atomic_inc(&mp->stats.xid_not_found);
+ goto rel;
+ }
+ sof = fr_sof(fp);
+ if (fc_sof_is_init(sof)) {
+ sp = fc_seq_start_next(&ep->seq);
+ sp->id = fh->fh_seq_id;
+ sp->ssb_stat |= SSB_ST_RESP;
+ } else {
+ sp = &ep->seq;
+ if (sp->id != fh->fh_seq_id) {
+ atomic_inc(&mp->stats.seq_not_found);
+ goto rel;
+ }
+ }
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ fr_seq(fp) = sp;
+ if (f_ctl & FC_FC_SEQ_INIT)
+ ep->esb_stat |= ESB_ST_SEQ_INIT;
+
+ if (fc_sof_needs_ack(sof))
+ fc_seq_send_ack(sp, fp);
+ resp = ep->resp;
+ ex_resp_arg = ep->arg;
+
+ if (fh->fh_type != FC_TYPE_FCP && fr_eof(fp) == FC_EOF_T &&
+ (f_ctl & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) ==
+ (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) {
+ spin_lock_bh(&ep->ex_lock);
+ rc = fc_exch_done_locked(ep);
+ WARN_ON(fc_seq_exch(sp) != ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_mgr_delete_ep(ep);
+ }
+
+ /*
+ * Call the receive function.
+ * The sequence is held (has a refcnt) for us,
+ * but not for the receive function.
+ *
+ * The receive function may allocate a new sequence
+ * over the old one, so we shouldn't change the
+ * sequence after this.
+ *
+ * The frame will be freed by the receive function.
+ * If new exch resp handler is valid then call that
+ * first.
+ */
+ if (resp)
+ resp(sp, fp, ex_resp_arg);
+ else
+ fc_frame_free(fp);
+ fc_exch_release(ep);
+ return;
+rel:
+ fc_exch_release(ep);
+out:
+ fc_frame_free(fp);
+}
+
+/*
+ * Handle receive for a sequence where other end is responding to our sequence.
+ */
+static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
+{
+ struct fc_seq *sp;
+
+ sp = fc_seq_lookup_orig(mp, fp); /* doesn't hold sequence */
+ if (!sp) {
+ atomic_inc(&mp->stats.xid_not_found);
+ FC_DEBUG_EXCH("seq lookup failed\n");
+ } else {
+ atomic_inc(&mp->stats.non_bls_resp);
+ FC_DEBUG_EXCH("non-BLS response to sequence");
+ }
+ fc_frame_free(fp);
+}
+
+/*
+ * Handle the response to an ABTS for exchange or sequence.
+ * This can be BA_ACC or BA_RJT.
+ */
+static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
+{
+ void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
+ void *ex_resp_arg;
+ struct fc_frame_header *fh;
+ struct fc_ba_acc *ap;
+ struct fc_seq *sp;
+ u16 low;
+ u16 high;
+ int rc = 1, has_rec = 0;
+
+ fh = fc_frame_header_get(fp);
+ FC_DEBUG_EXCH("exch: BLS rctl %x - %s\n",
+ fh->fh_r_ctl, fc_exch_rctl_name(fh->fh_r_ctl));
+
+ if (cancel_delayed_work_sync(&ep->timeout_work))
+ fc_exch_release(ep); /* release from pending timer hold */
+
+ spin_lock_bh(&ep->ex_lock);
+ switch (fh->fh_r_ctl) {
+ case FC_RCTL_BA_ACC:
+ ap = fc_frame_payload_get(fp, sizeof(*ap));
+ if (!ap)
+ break;
+
+ /*
+ * Decide whether to establish a Recovery Qualifier.
+ * We do this if there is a non-empty SEQ_CNT range and
+ * SEQ_ID is the same as the one we aborted.
+ */
+ low = ntohs(ap->ba_low_seq_cnt);
+ high = ntohs(ap->ba_high_seq_cnt);
+ if ((ep->esb_stat & ESB_ST_REC_QUAL) == 0 &&
+ (ap->ba_seq_id_val != FC_BA_SEQ_ID_VAL ||
+ ap->ba_seq_id == ep->seq_id) && low != high) {
+ ep->esb_stat |= ESB_ST_REC_QUAL;
+ fc_exch_hold(ep); /* hold for recovery qualifier */
+ has_rec = 1;
+ }
+ break;
+ case FC_RCTL_BA_RJT:
+ break;
+ default:
+ break;
+ }
+
+ resp = ep->resp;
+ ex_resp_arg = ep->arg;
+
+ /* do we need to do some other checks here. Can we reuse more of
+ * fc_exch_recv_seq_resp
+ */
+ sp = &ep->seq;
+ /*
+ * do we want to check END_SEQ as well as LAST_SEQ here?
+ */
+ if (ep->fh_type != FC_TYPE_FCP &&
+ ntoh24(fh->fh_f_ctl) & FC_FC_LAST_SEQ)
+ rc = fc_exch_done_locked(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_mgr_delete_ep(ep);
+
+ if (resp)
+ resp(sp, fp, ex_resp_arg);
+ else
+ fc_frame_free(fp);
+
+ if (has_rec)
+ fc_exch_timer_set(ep, ep->r_a_tov);
+
+}
+
+/*
+ * Receive BLS sequence.
+ * This is always a sequence initiated by the remote side.
+ * We may be either the originator or recipient of the exchange.
+ */
+static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
+{
+ struct fc_frame_header *fh;
+ struct fc_exch *ep;
+ u32 f_ctl;
+
+ fh = fc_frame_header_get(fp);
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ fr_seq(fp) = NULL;
+
+ ep = fc_exch_find(mp, (f_ctl & FC_FC_EX_CTX) ?
+ ntohs(fh->fh_ox_id) : ntohs(fh->fh_rx_id));
+ if (ep && (f_ctl & FC_FC_SEQ_INIT)) {
+ spin_lock_bh(&ep->ex_lock);
+ ep->esb_stat |= ESB_ST_SEQ_INIT;
+ spin_unlock_bh(&ep->ex_lock);
+ }
+ if (f_ctl & FC_FC_SEQ_CTX) {
+ /*
+ * A response to a sequence we initiated.
+ * This should only be ACKs for class 2 or F.
+ */
+ switch (fh->fh_r_ctl) {
+ case FC_RCTL_ACK_1:
+ case FC_RCTL_ACK_0:
+ break;
+ default:
+ FC_DEBUG_EXCH("BLS rctl %x - %s received",
+ fh->fh_r_ctl,
+ fc_exch_rctl_name(fh->fh_r_ctl));
+ break;
+ }
+ fc_frame_free(fp);
+ } else {
+ switch (fh->fh_r_ctl) {
+ case FC_RCTL_BA_RJT:
+ case FC_RCTL_BA_ACC:
+ if (ep)
+ fc_exch_abts_resp(ep, fp);
+ else
+ fc_frame_free(fp);
+ break;
+ case FC_RCTL_BA_ABTS:
+ fc_exch_recv_abts(ep, fp);
+ break;
+ default: /* ignore junk */
+ fc_frame_free(fp);
+ break;
+ }
+ }
+ if (ep)
+ fc_exch_release(ep); /* release hold taken by fc_exch_find */
+}
+
+/*
+ * Accept sequence with LS_ACC.
+ * If this fails due to allocation or transmit congestion, assume the
+ * originator will repeat the sequence.
+ */
+static void fc_seq_ls_acc(struct fc_seq *req_sp)
+{
+ struct fc_seq *sp;
+ struct fc_els_ls_acc *acc;
+ struct fc_frame *fp;
+
+ sp = fc_seq_start_next(req_sp);
+ fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*acc));
+ if (fp) {
+ acc = fc_frame_payload_get(fp, sizeof(*acc));
+ memset(acc, 0, sizeof(*acc));
+ acc->la_cmd = ELS_LS_ACC;
+ fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
+ }
+}
+
+/*
+ * Reject sequence with ELS LS_RJT.
+ * If this fails due to allocation or transmit congestion, assume the
+ * originator will repeat the sequence.
+ */
+static void fc_seq_ls_rjt(struct fc_seq *req_sp, enum fc_els_rjt_reason reason,
+ enum fc_els_rjt_explan explan)
+{
+ struct fc_seq *sp;
+ struct fc_els_ls_rjt *rjt;
+ struct fc_frame *fp;
+
+ sp = fc_seq_start_next(req_sp);
+ fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*rjt));
+ if (fp) {
+ rjt = fc_frame_payload_get(fp, sizeof(*rjt));
+ memset(rjt, 0, sizeof(*rjt));
+ rjt->er_cmd = ELS_LS_RJT;
+ rjt->er_reason = reason;
+ rjt->er_explan = explan;
+ fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
+ }
+}
+
+static void fc_exch_reset(struct fc_exch *ep)
+{
+ struct fc_seq *sp;
+ void (*resp)(struct fc_seq *, struct fc_frame *, void *);
+ void *arg;
+ int rc = 1;
+
+ spin_lock_bh(&ep->ex_lock);
+ ep->state |= FC_EX_RST_CLEANUP;
+ /*
+ * we really want to call del_timer_sync, but cannot due
+ * to the lport calling with the lport lock held (some resp
+ * functions can also grab the lport lock which could cause
+ * a deadlock).
+ */
+ if (cancel_delayed_work(&ep->timeout_work))
+ atomic_dec(&ep->ex_refcnt); /* drop hold for timer */
+ resp = ep->resp;
+ ep->resp = NULL;
+ if (ep->esb_stat & ESB_ST_REC_QUAL)
+ atomic_dec(&ep->ex_refcnt); /* drop hold for rec_qual */
+ ep->esb_stat &= ~ESB_ST_REC_QUAL;
+ arg = ep->arg;
+ sp = &ep->seq;
+ rc = fc_exch_done_locked(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_mgr_delete_ep(ep);
+
+ if (resp)
+ resp(sp, ERR_PTR(-FC_EX_CLOSED), arg);
+}
+
+/*
+ * Reset an exchange manager, releasing all sequences and exchanges.
+ * If sid is non-zero, reset only exchanges we source from that FID.
+ * If did is non-zero, reset only exchanges destined to that FID.
+ */
+void fc_exch_mgr_reset(struct fc_exch_mgr *mp, u32 sid, u32 did)
+{
+ struct fc_exch *ep;
+ struct fc_exch *next;
+
+ spin_lock_bh(&mp->em_lock);
+restart:
+ list_for_each_entry_safe(ep, next, &mp->ex_list, ex_list) {
+ if ((sid == 0 || sid == ep->sid) &&
+ (did == 0 || did == ep->did)) {
+ fc_exch_hold(ep);
+ spin_unlock_bh(&mp->em_lock);
+
+ fc_exch_reset(ep);
+
+ fc_exch_release(ep);
+ spin_lock_bh(&mp->em_lock);
+
+ /*
+ * must restart loop incase while lock was down
+ * multiple eps were released.
+ */
+ goto restart;
+ }
+ }
+ spin_unlock_bh(&mp->em_lock);
+}
+EXPORT_SYMBOL(fc_exch_mgr_reset);
+
+/*
+ * Handle incoming ELS REC - Read Exchange Concise.
+ * Note that the requesting port may be different than the S_ID in the request.
+ */
+static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)
+{
+ struct fc_frame *fp;
+ struct fc_exch *ep;
+ struct fc_exch_mgr *em;
+ struct fc_els_rec *rp;
+ struct fc_els_rec_acc *acc;
+ enum fc_els_rjt_reason reason = ELS_RJT_LOGIC;
+ enum fc_els_rjt_explan explan;
+ u32 sid;
+ u16 rxid;
+ u16 oxid;
+
+ rp = fc_frame_payload_get(rfp, sizeof(*rp));
+ explan = ELS_EXPL_INV_LEN;
+ if (!rp)
+ goto reject;
+ sid = ntoh24(rp->rec_s_id);
+ rxid = ntohs(rp->rec_rx_id);
+ oxid = ntohs(rp->rec_ox_id);
+
+ /*
+ * Currently it's hard to find the local S_ID from the exchange
+ * manager. This will eventually be fixed, but for now it's easier
+ * to lookup the subject exchange twice, once as if we were
+ * the initiator, and then again if we weren't.
+ */
+ em = fc_seq_exch(sp)->em;
+ ep = fc_exch_find(em, oxid);
+ explan = ELS_EXPL_OXID_RXID;
+ if (ep && ep->oid == sid) {
+ if (ep->rxid != FC_XID_UNKNOWN &&
+ rxid != FC_XID_UNKNOWN &&
+ ep->rxid != rxid)
+ goto rel;
+ } else {
+ if (ep)
+ fc_exch_release(ep);
+ ep = NULL;
+ if (rxid != FC_XID_UNKNOWN)
+ ep = fc_exch_find(em, rxid);
+ if (!ep)
+ goto reject;
+ }
+
+ fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*acc));
+ if (!fp) {
+ fc_exch_done(sp);
+ goto out;
+ }
+ sp = fc_seq_start_next(sp);
+ acc = fc_frame_payload_get(fp, sizeof(*acc));
+ memset(acc, 0, sizeof(*acc));
+ acc->reca_cmd = ELS_LS_ACC;
+ acc->reca_ox_id = rp->rec_ox_id;
+ memcpy(acc->reca_ofid, rp->rec_s_id, 3);
+ acc->reca_rx_id = htons(ep->rxid);
+ if (ep->sid == ep->oid)
+ hton24(acc->reca_rfid, ep->did);
+ else
+ hton24(acc->reca_rfid, ep->sid);
+ acc->reca_fc4value = htonl(ep->seq.rec_data);
+ acc->reca_e_stat = htonl(ep->esb_stat & (ESB_ST_RESP |
+ ESB_ST_SEQ_INIT |
+ ESB_ST_COMPLETE));
+ sp = fc_seq_start_next(sp);
+ fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
+out:
+ fc_exch_release(ep);
+ fc_frame_free(rfp);
+ return;
+
+rel:
+ fc_exch_release(ep);
+reject:
+ fc_seq_ls_rjt(sp, reason, explan);
+ fc_frame_free(rfp);
+}
+
+/*
+ * Handle response from RRQ.
+ * Not much to do here, really.
+ * Should report errors.
+ *
+ * TODO: fix error handler.
+ */
+static void fc_exch_rrq_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg)
+{
+ struct fc_exch *aborted_ep = arg;
+ unsigned int op;
+
+ if (IS_ERR(fp)) {
+ int err = PTR_ERR(fp);
+
+ if (err == -FC_EX_CLOSED)
+ goto cleanup;
+ FC_DBG("Cannot process RRQ, because of frame error %d\n", err);
+ return;
+ }
+
+ op = fc_frame_payload_op(fp);
+ fc_frame_free(fp);
+
+ switch (op) {
+ case ELS_LS_RJT:
+ FC_DBG("LS_RJT for RRQ");
+ /* fall through */
+ case ELS_LS_ACC:
+ goto cleanup;
+ default:
+ FC_DBG("unexpected response op %x for RRQ", op);
+ return;
+ }
+
+cleanup:
+ fc_exch_done(&aborted_ep->seq);
+ /* drop hold for rec qual */
+ fc_exch_release(aborted_ep);
+}
+
+/*
+ * Send ELS RRQ - Reinstate Recovery Qualifier.
+ * This tells the remote port to stop blocking the use of
+ * the exchange and the seq_cnt range.
+ */
+static void fc_exch_rrq(struct fc_exch *ep)
+{
+ struct fc_lport *lp;
+ struct fc_els_rrq *rrq;
+ struct fc_frame *fp;
+ struct fc_seq *rrq_sp;
+ u32 did;
+
+ lp = ep->lp;
+
+ fp = fc_frame_alloc(lp, sizeof(*rrq));
+ if (!fp)
+ return;
+ rrq = fc_frame_payload_get(fp, sizeof(*rrq));
+ memset(rrq, 0, sizeof(*rrq));
+ rrq->rrq_cmd = ELS_RRQ;
+ hton24(rrq->rrq_s_id, ep->sid);
+ rrq->rrq_ox_id = htons(ep->oxid);
+ rrq->rrq_rx_id = htons(ep->rxid);
+
+ did = ep->did;
+ if (ep->esb_stat & ESB_ST_RESP)
+ did = ep->sid;
+
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, did,
+ fc_host_port_id(lp->host), FC_TYPE_ELS,
+ FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+ rrq_sp = fc_exch_seq_send(lp, fp, fc_exch_rrq_resp, NULL, ep,
+ lp->e_d_tov);
+ if (!rrq_sp) {
+ ep->esb_stat |= ESB_ST_REC_QUAL;
+ fc_exch_timer_set_locked(ep, ep->r_a_tov);
+ return;
+ }
+}
+
+
+/*
+ * Handle incoming ELS RRQ - Reset Recovery Qualifier.
+ */
+static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
+{
+ struct fc_exch *ep; /* request or subject exchange */
+ struct fc_els_rrq *rp;
+ u32 sid;
+ u16 xid;
+ enum fc_els_rjt_explan explan;
+
+ rp = fc_frame_payload_get(fp, sizeof(*rp));
+ explan = ELS_EXPL_INV_LEN;
+ if (!rp)
+ goto reject;
+
+ /*
+ * lookup subject exchange.
+ */
+ ep = fc_seq_exch(sp);
+ sid = ntoh24(rp->rrq_s_id); /* subject source */
+ xid = ep->did == sid ? ntohs(rp->rrq_ox_id) : ntohs(rp->rrq_rx_id);
+ ep = fc_exch_find(ep->em, xid);
+
+ explan = ELS_EXPL_OXID_RXID;
+ if (!ep)
+ goto reject;
+ spin_lock_bh(&ep->ex_lock);
+ if (ep->oxid != ntohs(rp->rrq_ox_id))
+ goto unlock_reject;
+ if (ep->rxid != ntohs(rp->rrq_rx_id) &&
+ ep->rxid != FC_XID_UNKNOWN)
+ goto unlock_reject;
+ explan = ELS_EXPL_SID;
+ if (ep->sid != sid)
+ goto unlock_reject;
+
+ /*
+ * Clear Recovery Qualifier state, and cancel timer if complete.
+ */
+ if (ep->esb_stat & ESB_ST_REC_QUAL) {
+ ep->esb_stat &= ~ESB_ST_REC_QUAL;
+ atomic_dec(&ep->ex_refcnt); /* drop hold for rec qual */
+ }
+ if (ep->esb_stat & ESB_ST_COMPLETE) {
+ if (cancel_delayed_work(&ep->timeout_work))
+ atomic_dec(&ep->ex_refcnt); /* drop timer hold */
+ }
+
+ spin_unlock_bh(&ep->ex_lock);
+
+ /*
+ * Send LS_ACC.
+ */
+ fc_seq_ls_acc(sp);
+ fc_frame_free(fp);
+ return;
+
+unlock_reject:
+ spin_unlock_bh(&ep->ex_lock);
+ fc_exch_release(ep); /* drop hold from fc_exch_find */
+reject:
+ fc_seq_ls_rjt(sp, ELS_RJT_LOGIC, explan);
+ fc_frame_free(fp);
+}
+
+struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
+ enum fc_class class,
+ u16 min_xid, u16 max_xid)
+{
+ struct fc_exch_mgr *mp;
+ size_t len;
+
+ if (max_xid <= min_xid || min_xid == 0 || max_xid == FC_XID_UNKNOWN) {
+ FC_DBG("Invalid min_xid 0x:%x and max_xid 0x:%x\n",
+ min_xid, max_xid);
+ return NULL;
+ }
+
+ /*
+ * Memory need for EM
+ */
+#define xid_ok(i, m1, m2) (((i) >= (m1)) && ((i) <= (m2)))
+ len = (max_xid - min_xid + 1) * (sizeof(struct fc_exch *));
+ len += sizeof(struct fc_exch_mgr);
+
+ mp = kzalloc(len, GFP_ATOMIC);
+ if (!mp)
+ return NULL;
+
+ mp->class = class;
+ mp->total_exches = 0;
+ mp->exches = (struct fc_exch **)(mp + 1);
+ mp->lp = lp;
+ /* adjust em exch xid range for offload */
+ mp->min_xid = min_xid;
+ mp->max_xid = max_xid;
+ mp->last_xid = min_xid - 1;
+ mp->max_read = 0;
+ mp->last_read = 0;
+ if (lp->lro_enabled && xid_ok(lp->lro_xid, min_xid, max_xid)) {
+ mp->max_read = lp->lro_xid;
+ mp->last_read = min_xid - 1;
+ mp->last_xid = mp->max_read;
+ } else {
+ /* disable lro if no xid control over read */
+ lp->lro_enabled = 0;
+ }
+
+ INIT_LIST_HEAD(&mp->ex_list);
+ spin_lock_init(&mp->em_lock);
+
+ mp->ep_pool = mempool_create_slab_pool(2, fc_em_cachep);
+ if (!mp->ep_pool)
+ goto free_mp;
+
+ return mp;
+
+free_mp:
+ kfree(mp);
+ return NULL;
+}
+EXPORT_SYMBOL(fc_exch_mgr_alloc);
+
+void fc_exch_mgr_free(struct fc_exch_mgr *mp)
+{
+ WARN_ON(!mp);
+ /*
+ * The total exch count must be zero
+ * before freeing exchange manager.
+ */
+ WARN_ON(mp->total_exches != 0);
+ mempool_destroy(mp->ep_pool);
+ kfree(mp);
+}
+EXPORT_SYMBOL(fc_exch_mgr_free);
+
+struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp)
+{
+ if (!lp || !lp->emp)
+ return NULL;
+
+ return fc_exch_alloc(lp->emp, fp, 0);
+}
+EXPORT_SYMBOL(fc_exch_get);
+
+struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
+ struct fc_frame *fp,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *fp,
+ void *arg),
+ void (*destructor)(struct fc_seq *, void *),
+ void *arg, u32 timer_msec)
+{
+ struct fc_exch *ep;
+ struct fc_seq *sp = NULL;
+ struct fc_frame_header *fh;
+ int rc = 1;
+
+ ep = lp->tt.exch_get(lp, fp);
+ if (!ep) {
+ fc_frame_free(fp);
+ return NULL;
+ }
+ ep->esb_stat |= ESB_ST_SEQ_INIT;
+ fh = fc_frame_header_get(fp);
+ fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id));
+ ep->resp = resp;
+ ep->destructor = destructor;
+ ep->arg = arg;
+ ep->r_a_tov = FC_DEF_R_A_TOV;
+ ep->lp = lp;
+ sp = &ep->seq;
+
+ ep->fh_type = fh->fh_type; /* save for possbile timeout handling */
+ ep->f_ctl = ntoh24(fh->fh_f_ctl);
+ fc_exch_setup_hdr(ep, fp, ep->f_ctl);
+ sp->cnt++;
+
+ if (unlikely(lp->tt.frame_send(lp, fp)))
+ goto err;
+
+ if (timer_msec)
+ fc_exch_timer_set_locked(ep, timer_msec);
+ ep->f_ctl &= ~FC_FC_FIRST_SEQ; /* not first seq */
+
+ if (ep->f_ctl & FC_FC_SEQ_INIT)
+ ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+ spin_unlock_bh(&ep->ex_lock);
+ return sp;
+err:
+ rc = fc_exch_done_locked(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_mgr_delete_ep(ep);
+ return NULL;
+}
+EXPORT_SYMBOL(fc_exch_seq_send);
+
+/*
+ * Receive a frame
+ */
+void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ u32 f_ctl;
+
+ /* lport lock ? */
+ if (!lp || !mp || (lp->state == LPORT_ST_NONE)) {
+ FC_DBG("fc_lport or EM is not allocated and configured");
+ fc_frame_free(fp);
+ return;
+ }
+
+ /*
+ * If frame is marked invalid, just drop it.
+ */
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ switch (fr_eof(fp)) {
+ case FC_EOF_T:
+ if (f_ctl & FC_FC_END_SEQ)
+ skb_trim(fp_skb(fp), fr_len(fp) - FC_FC_FILL(f_ctl));
+ /* fall through */
+ case FC_EOF_N:
+ if (fh->fh_type == FC_TYPE_BLS)
+ fc_exch_recv_bls(mp, fp);
+ else if ((f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) ==
+ FC_FC_EX_CTX)
+ fc_exch_recv_seq_resp(mp, fp);
+ else if (f_ctl & FC_FC_SEQ_CTX)
+ fc_exch_recv_resp(mp, fp);
+ else
+ fc_exch_recv_req(lp, mp, fp);
+ break;
+ default:
+ FC_DBG("dropping invalid frame (eof %x)", fr_eof(fp));
+ fc_frame_free(fp);
+ break;
+ }
+}
+EXPORT_SYMBOL(fc_exch_recv);
+
+int fc_exch_init(struct fc_lport *lp)
+{
+ if (!lp->tt.exch_get) {
+ /*
+ * exch_put() should be NULL if
+ * exch_get() is NULL
+ */
+ WARN_ON(lp->tt.exch_put);
+ lp->tt.exch_get = fc_exch_get;
+ }
+
+ if (!lp->tt.seq_start_next)
+ lp->tt.seq_start_next = fc_seq_start_next;
+
+ if (!lp->tt.exch_seq_send)
+ lp->tt.exch_seq_send = fc_exch_seq_send;
+
+ if (!lp->tt.seq_send)
+ lp->tt.seq_send = fc_seq_send;
+
+ if (!lp->tt.seq_els_rsp_send)
+ lp->tt.seq_els_rsp_send = fc_seq_els_rsp_send;
+
+ if (!lp->tt.exch_done)
+ lp->tt.exch_done = fc_exch_done;
+
+ if (!lp->tt.exch_mgr_reset)
+ lp->tt.exch_mgr_reset = fc_exch_mgr_reset;
+
+ if (!lp->tt.seq_exch_abort)
+ lp->tt.seq_exch_abort = fc_seq_exch_abort;
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_exch_init);
+
+int fc_setup_exch_mgr(void)
+{
+ fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!fc_em_cachep)
+ return -ENOMEM;
+ return 0;
+}
+
+void fc_destroy_exch_mgr(void)
+{
+ kmem_cache_destroy(fc_em_cachep);
+}
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
new file mode 100644
index 000000000000..404e63ff46b8
--- /dev/null
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -0,0 +1,2131 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 Red Hat, Inc. All rights reserved.
+ * Copyright(c) 2008 Mike Christie
+ *
+ * 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/scatterlist.h>
+#include <linux/err.h>
+#include <linux/crc32.h>
+
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+
+#include <scsi/fc/fc_fc2.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("libfc");
+MODULE_LICENSE("GPL");
+
+static int fc_fcp_debug;
+
+#define FC_DEBUG_FCP(fmt...) \
+ do { \
+ if (fc_fcp_debug) \
+ FC_DBG(fmt); \
+ } while (0)
+
+static struct kmem_cache *scsi_pkt_cachep;
+
+/* SRB state definitions */
+#define FC_SRB_FREE 0 /* cmd is free */
+#define FC_SRB_CMD_SENT (1 << 0) /* cmd has been sent */
+#define FC_SRB_RCV_STATUS (1 << 1) /* response has arrived */
+#define FC_SRB_ABORT_PENDING (1 << 2) /* cmd abort sent to device */
+#define FC_SRB_ABORTED (1 << 3) /* abort acknowleged */
+#define FC_SRB_DISCONTIG (1 << 4) /* non-sequential data recvd */
+#define FC_SRB_COMPL (1 << 5) /* fc_io_compl has been run */
+#define FC_SRB_FCP_PROCESSING_TMO (1 << 6) /* timer function processing */
+#define FC_SRB_NOMEM (1 << 7) /* dropped to out of mem */
+
+#define FC_SRB_READ (1 << 1)
+#define FC_SRB_WRITE (1 << 0)
+
+/*
+ * The SCp.ptr should be tested and set under the host lock. NULL indicates
+ * that the command has been retruned to the scsi layer.
+ */
+#define CMD_SP(Cmnd) ((struct fc_fcp_pkt *)(Cmnd)->SCp.ptr)
+#define CMD_ENTRY_STATUS(Cmnd) ((Cmnd)->SCp.have_data_in)
+#define CMD_COMPL_STATUS(Cmnd) ((Cmnd)->SCp.this_residual)
+#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status)
+#define CMD_RESID_LEN(Cmnd) ((Cmnd)->SCp.buffers_residual)
+
+struct fc_fcp_internal {
+ mempool_t *scsi_pkt_pool;
+ struct list_head scsi_pkt_queue;
+ u8 throttled;
+};
+
+#define fc_get_scsi_internal(x) ((struct fc_fcp_internal *)(x)->scsi_priv)
+
+/*
+ * function prototypes
+ * FC scsi I/O related functions
+ */
+static void fc_fcp_recv_data(struct fc_fcp_pkt *, struct fc_frame *);
+static void fc_fcp_recv(struct fc_seq *, struct fc_frame *, void *);
+static void fc_fcp_resp(struct fc_fcp_pkt *, struct fc_frame *);
+static void fc_fcp_complete_locked(struct fc_fcp_pkt *);
+static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *);
+static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp);
+static void fc_timeout_error(struct fc_fcp_pkt *);
+static void fc_fcp_timeout(unsigned long data);
+static void fc_fcp_rec(struct fc_fcp_pkt *);
+static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *);
+static void fc_fcp_rec_resp(struct fc_seq *, struct fc_frame *, void *);
+static void fc_io_compl(struct fc_fcp_pkt *);
+
+static void fc_fcp_srr(struct fc_fcp_pkt *, enum fc_rctl, u32);
+static void fc_fcp_srr_resp(struct fc_seq *, struct fc_frame *, void *);
+static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);
+
+/*
+ * command status codes
+ */
+#define FC_COMPLETE 0
+#define FC_CMD_ABORTED 1
+#define FC_CMD_RESET 2
+#define FC_CMD_PLOGO 3
+#define FC_SNS_RCV 4
+#define FC_TRANS_ERR 5
+#define FC_DATA_OVRRUN 6
+#define FC_DATA_UNDRUN 7
+#define FC_ERROR 8
+#define FC_HRD_ERROR 9
+#define FC_CMD_TIME_OUT 10
+
+/*
+ * Error recovery timeout values.
+ */
+#define FC_SCSI_ER_TIMEOUT (10 * HZ)
+#define FC_SCSI_TM_TOV (10 * HZ)
+#define FC_SCSI_REC_TOV (2 * HZ)
+#define FC_HOST_RESET_TIMEOUT (30 * HZ)
+
+#define FC_MAX_ERROR_CNT 5
+#define FC_MAX_RECOV_RETRY 3
+
+#define FC_FCP_DFLT_QUEUE_DEPTH 32
+
+/**
+ * fc_fcp_pkt_alloc - allocation routine for scsi_pkt packet
+ * @lp: fc lport struct
+ * @gfp: gfp flags for allocation
+ *
+ * This is used by upper layer scsi driver.
+ * Return Value : scsi_pkt structure or null on allocation failure.
+ * Context : call from process context. no locking required.
+ */
+static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ struct fc_fcp_pkt *fsp;
+
+ fsp = mempool_alloc(si->scsi_pkt_pool, gfp);
+ if (fsp) {
+ memset(fsp, 0, sizeof(*fsp));
+ fsp->lp = lp;
+ atomic_set(&fsp->ref_cnt, 1);
+ init_timer(&fsp->timer);
+ INIT_LIST_HEAD(&fsp->list);
+ spin_lock_init(&fsp->scsi_pkt_lock);
+ }
+ return fsp;
+}
+
+/**
+ * fc_fcp_pkt_release - release hold on scsi_pkt packet
+ * @fsp: fcp packet struct
+ *
+ * This is used by upper layer scsi driver.
+ * Context : call from process and interrupt context.
+ * no locking required
+ */
+static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp)
+{
+ if (atomic_dec_and_test(&fsp->ref_cnt)) {
+ struct fc_fcp_internal *si = fc_get_scsi_internal(fsp->lp);
+
+ mempool_free(fsp, si->scsi_pkt_pool);
+ }
+}
+
+static void fc_fcp_pkt_hold(struct fc_fcp_pkt *fsp)
+{
+ atomic_inc(&fsp->ref_cnt);
+}
+
+/**
+ * fc_fcp_pkt_destory - release hold on scsi_pkt packet
+ *
+ * @seq: exchange sequence
+ * @fsp: fcp packet struct
+ *
+ * Release hold on scsi_pkt packet set to keep scsi_pkt
+ * till EM layer exch resource is not freed.
+ * Context : called from from EM layer.
+ * no locking required
+ */
+static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp)
+{
+ fc_fcp_pkt_release(fsp);
+}
+
+/**
+ * fc_fcp_lock_pkt - lock a packet and get a ref to it.
+ * @fsp: fcp packet
+ *
+ * We should only return error if we return a command to scsi-ml before
+ * getting a response. This can happen in cases where we send a abort, but
+ * do not wait for the response and the abort and command can be passing
+ * each other on the wire/network-layer.
+ *
+ * Note: this function locks the packet and gets a reference to allow
+ * callers to call the completion function while the lock is held and
+ * not have to worry about the packets refcount.
+ *
+ * TODO: Maybe we should just have callers grab/release the lock and
+ * have a function that they call to verify the fsp and grab a ref if
+ * needed.
+ */
+static inline int fc_fcp_lock_pkt(struct fc_fcp_pkt *fsp)
+{
+ spin_lock_bh(&fsp->scsi_pkt_lock);
+ if (fsp->state & FC_SRB_COMPL) {
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+ return -EPERM;
+ }
+
+ fc_fcp_pkt_hold(fsp);
+ return 0;
+}
+
+static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp)
+{
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+ fc_fcp_pkt_release(fsp);
+}
+
+static void fc_fcp_timer_set(struct fc_fcp_pkt *fsp, unsigned long delay)
+{
+ if (!(fsp->state & FC_SRB_COMPL))
+ mod_timer(&fsp->timer, jiffies + delay);
+}
+
+static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp)
+{
+ if (!fsp->seq_ptr)
+ return -EINVAL;
+
+ fsp->state |= FC_SRB_ABORT_PENDING;
+ return fsp->lp->tt.seq_exch_abort(fsp->seq_ptr, 0);
+}
+
+/*
+ * Retry command.
+ * An abort isn't needed.
+ */
+static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp)
+{
+ if (fsp->seq_ptr) {
+ fsp->lp->tt.exch_done(fsp->seq_ptr);
+ fsp->seq_ptr = NULL;
+ }
+
+ fsp->state &= ~FC_SRB_ABORT_PENDING;
+ fsp->io_status = SUGGEST_RETRY << 24;
+ fsp->status_code = FC_ERROR;
+ fc_fcp_complete_locked(fsp);
+}
+
+/*
+ * Receive SCSI data from target.
+ * Called after receiving solicited data.
+ */
+static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
+{
+ struct scsi_cmnd *sc = fsp->cmd;
+ struct fc_lport *lp = fsp->lp;
+ struct fcoe_dev_stats *stats;
+ struct fc_frame_header *fh;
+ size_t start_offset;
+ size_t offset;
+ u32 crc;
+ u32 copy_len = 0;
+ size_t len;
+ void *buf;
+ struct scatterlist *sg;
+ size_t remaining;
+
+ fh = fc_frame_header_get(fp);
+ offset = ntohl(fh->fh_parm_offset);
+ start_offset = offset;
+ len = fr_len(fp) - sizeof(*fh);
+ buf = fc_frame_payload_get(fp, 0);
+
+ if (offset + len > fsp->data_len) {
+ /*
+ * this should never happen
+ */
+ if ((fr_flags(fp) & FCPHF_CRC_UNCHECKED) &&
+ fc_frame_crc_check(fp))
+ goto crc_err;
+ FC_DEBUG_FCP("data received past end. len %zx offset %zx "
+ "data_len %x\n", len, offset, fsp->data_len);
+ fc_fcp_retry_cmd(fsp);
+ return;
+ }
+ if (offset != fsp->xfer_len)
+ fsp->state |= FC_SRB_DISCONTIG;
+
+ crc = 0;
+ if (fr_flags(fp) & FCPHF_CRC_UNCHECKED)
+ crc = crc32(~0, (u8 *) fh, sizeof(*fh));
+
+ sg = scsi_sglist(sc);
+ remaining = len;
+
+ while (remaining > 0 && sg) {
+ size_t off;
+ void *page_addr;
+ size_t sg_bytes;
+
+ if (offset >= sg->length) {
+ offset -= sg->length;
+ sg = sg_next(sg);
+ continue;
+ }
+ sg_bytes = min(remaining, sg->length - offset);
+
+ /*
+ * The scatterlist item may be bigger than PAGE_SIZE,
+ * but we are limited to mapping PAGE_SIZE at a time.
+ */
+ off = offset + sg->offset;
+ sg_bytes = min(sg_bytes, (size_t)
+ (PAGE_SIZE - (off & ~PAGE_MASK)));
+ page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT),
+ KM_SOFTIRQ0);
+ if (!page_addr)
+ break; /* XXX panic? */
+
+ if (fr_flags(fp) & FCPHF_CRC_UNCHECKED)
+ crc = crc32(crc, buf, sg_bytes);
+ memcpy((char *)page_addr + (off & ~PAGE_MASK), buf,
+ sg_bytes);
+
+ kunmap_atomic(page_addr, KM_SOFTIRQ0);
+ buf += sg_bytes;
+ offset += sg_bytes;
+ remaining -= sg_bytes;
+ copy_len += sg_bytes;
+ }
+
+ if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+ buf = fc_frame_payload_get(fp, 0);
+ if (len % 4) {
+ crc = crc32(crc, buf + len, 4 - (len % 4));
+ len += 4 - (len % 4);
+ }
+
+ if (~crc != le32_to_cpu(fr_crc(fp))) {
+crc_err:
+ stats = lp->dev_stats[smp_processor_id()];
+ stats->ErrorFrames++;
+ if (stats->InvalidCRCCount++ < 5)
+ FC_DBG("CRC error on data frame\n");
+ /*
+ * Assume the frame is total garbage.
+ * We may have copied it over the good part
+ * of the buffer.
+ * If so, we need to retry the entire operation.
+ * Otherwise, ignore it.
+ */
+ if (fsp->state & FC_SRB_DISCONTIG)
+ fc_fcp_retry_cmd(fsp);
+ return;
+ }
+ }
+
+ if (fsp->xfer_contig_end == start_offset)
+ fsp->xfer_contig_end += copy_len;
+ fsp->xfer_len += copy_len;
+
+ /*
+ * In the very rare event that this data arrived after the response
+ * and completes the transfer, call the completion handler.
+ */
+ if (unlikely(fsp->state & FC_SRB_RCV_STATUS) &&
+ fsp->xfer_len == fsp->data_len - fsp->scsi_resid)
+ fc_fcp_complete_locked(fsp);
+}
+
+/*
+ * fc_fcp_send_data - Send SCSI data to target.
+ * @fsp: ptr to fc_fcp_pkt
+ * @sp: ptr to this sequence
+ * @offset: starting offset for this data request
+ * @seq_blen: the burst length for this data request
+ *
+ * Called after receiving a Transfer Ready data descriptor.
+ * if LLD is capable of seq offload then send down seq_blen
+ * size of data in single frame, otherwise send multiple FC
+ * frames of max FC frame payload supported by target port.
+ *
+ * Returns : 0 for success.
+ */
+static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
+ size_t offset, size_t seq_blen)
+{
+ struct fc_exch *ep;
+ struct scsi_cmnd *sc;
+ struct scatterlist *sg;
+ struct fc_frame *fp = NULL;
+ struct fc_lport *lp = fsp->lp;
+ size_t remaining;
+ size_t t_blen;
+ size_t tlen;
+ size_t sg_bytes;
+ size_t frame_offset, fh_parm_offset;
+ int error;
+ void *data = NULL;
+ void *page_addr;
+ int using_sg = lp->sg_supp;
+ u32 f_ctl;
+
+ WARN_ON(seq_blen <= 0);
+ if (unlikely(offset + seq_blen > fsp->data_len)) {
+ /* this should never happen */
+ FC_DEBUG_FCP("xfer-ready past end. seq_blen %zx offset %zx\n",
+ seq_blen, offset);
+ fc_fcp_send_abort(fsp);
+ return 0;
+ } else if (offset != fsp->xfer_len) {
+ /* Out of Order Data Request - no problem, but unexpected. */
+ FC_DEBUG_FCP("xfer-ready non-contiguous. "
+ "seq_blen %zx offset %zx\n", seq_blen, offset);
+ }
+
+ /*
+ * if LLD is capable of seq_offload then set transport
+ * burst length (t_blen) to seq_blen, otherwise set t_blen
+ * to max FC frame payload previously set in fsp->max_payload.
+ */
+ t_blen = lp->seq_offload ? seq_blen : fsp->max_payload;
+ WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD);
+ if (t_blen > 512)
+ t_blen &= ~(512 - 1); /* round down to block size */
+ WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD); /* won't go below 256 */
+ sc = fsp->cmd;
+
+ remaining = seq_blen;
+ fh_parm_offset = frame_offset = offset;
+ tlen = 0;
+ seq = lp->tt.seq_start_next(seq);
+ f_ctl = FC_FC_REL_OFF;
+ WARN_ON(!seq);
+
+ /*
+ * If a get_page()/put_page() will fail, don't use sg lists
+ * in the fc_frame structure.
+ *
+ * The put_page() may be long after the I/O has completed
+ * in the case of FCoE, since the network driver does it
+ * via free_skb(). See the test in free_pages_check().
+ *
+ * Test this case with 'dd </dev/zero >/dev/st0 bs=64k'.
+ */
+ if (using_sg) {
+ for (sg = scsi_sglist(sc); sg; sg = sg_next(sg)) {
+ if (page_count(sg_page(sg)) == 0 ||
+ (sg_page(sg)->flags & (1 << PG_lru |
+ 1 << PG_private |
+ 1 << PG_locked |
+ 1 << PG_active |
+ 1 << PG_slab |
+ 1 << PG_swapcache |
+ 1 << PG_writeback |
+ 1 << PG_reserved |
+ 1 << PG_buddy))) {
+ using_sg = 0;
+ break;
+ }
+ }
+ }
+ sg = scsi_sglist(sc);
+
+ while (remaining > 0 && sg) {
+ if (offset >= sg->length) {
+ offset -= sg->length;
+ sg = sg_next(sg);
+ continue;
+ }
+ if (!fp) {
+ tlen = min(t_blen, remaining);
+
+ /*
+ * TODO. Temporary workaround. fc_seq_send() can't
+ * handle odd lengths in non-linear skbs.
+ * This will be the final fragment only.
+ */
+ if (tlen % 4)
+ using_sg = 0;
+ if (using_sg) {
+ fp = _fc_frame_alloc(lp, 0);
+ if (!fp)
+ return -ENOMEM;
+ } else {
+ fp = fc_frame_alloc(lp, tlen);
+ if (!fp)
+ return -ENOMEM;
+
+ data = (void *)(fr_hdr(fp)) +
+ sizeof(struct fc_frame_header);
+ }
+ fh_parm_offset = frame_offset;
+ fr_max_payload(fp) = fsp->max_payload;
+ }
+ sg_bytes = min(tlen, sg->length - offset);
+ if (using_sg) {
+ WARN_ON(skb_shinfo(fp_skb(fp))->nr_frags >
+ FC_FRAME_SG_LEN);
+ get_page(sg_page(sg));
+ skb_fill_page_desc(fp_skb(fp),
+ skb_shinfo(fp_skb(fp))->nr_frags,
+ sg_page(sg), sg->offset + offset,
+ sg_bytes);
+ fp_skb(fp)->data_len += sg_bytes;
+ fr_len(fp) += sg_bytes;
+ fp_skb(fp)->truesize += PAGE_SIZE;
+ } else {
+ size_t off = offset + sg->offset;
+
+ /*
+ * The scatterlist item may be bigger than PAGE_SIZE,
+ * but we must not cross pages inside the kmap.
+ */
+ sg_bytes = min(sg_bytes, (size_t) (PAGE_SIZE -
+ (off & ~PAGE_MASK)));
+ page_addr = kmap_atomic(sg_page(sg) +
+ (off >> PAGE_SHIFT),
+ KM_SOFTIRQ0);
+ memcpy(data, (char *)page_addr + (off & ~PAGE_MASK),
+ sg_bytes);
+ kunmap_atomic(page_addr, KM_SOFTIRQ0);
+ data += sg_bytes;
+ }
+ offset += sg_bytes;
+ frame_offset += sg_bytes;
+ tlen -= sg_bytes;
+ remaining -= sg_bytes;
+
+ if (tlen)
+ continue;
+
+ /*
+ * Send sequence with transfer sequence initiative in case
+ * this is last FCP frame of the sequence.
+ */
+ if (remaining == 0)
+ f_ctl |= FC_FC_SEQ_INIT | FC_FC_END_SEQ;
+
+ ep = fc_seq_exch(seq);
+ fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
+ FC_TYPE_FCP, f_ctl, fh_parm_offset);
+
+ /*
+ * send fragment using for a sequence.
+ */
+ error = lp->tt.seq_send(lp, seq, fp);
+ if (error) {
+ WARN_ON(1); /* send error should be rare */
+ fc_fcp_retry_cmd(fsp);
+ return 0;
+ }
+ fp = NULL;
+ }
+ fsp->xfer_len += seq_blen; /* premature count? */
+ return 0;
+}
+
+static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
+{
+ int ba_done = 1;
+ struct fc_ba_rjt *brp;
+ struct fc_frame_header *fh;
+
+ fh = fc_frame_header_get(fp);
+ switch (fh->fh_r_ctl) {
+ case FC_RCTL_BA_ACC:
+ break;
+ case FC_RCTL_BA_RJT:
+ brp = fc_frame_payload_get(fp, sizeof(*brp));
+ if (brp && brp->br_reason == FC_BA_RJT_LOG_ERR)
+ break;
+ /* fall thru */
+ default:
+ /*
+ * we will let the command timeout
+ * and scsi-ml recover in this case,
+ * therefore cleared the ba_done flag.
+ */
+ ba_done = 0;
+ }
+
+ if (ba_done) {
+ fsp->state |= FC_SRB_ABORTED;
+ fsp->state &= ~FC_SRB_ABORT_PENDING;
+
+ if (fsp->wait_for_comp)
+ complete(&fsp->tm_done);
+ else
+ fc_fcp_complete_locked(fsp);
+ }
+}
+
+/*
+ * fc_fcp_reduce_can_queue - drop can_queue
+ * @lp: lport to drop queueing for
+ *
+ * If we are getting memory allocation failures, then we may
+ * be trying to execute too many commands. We let the running
+ * commands complete or timeout, then try again with a reduced
+ * can_queue. Eventually we will hit the point where we run
+ * on all reserved structs.
+ */
+static void fc_fcp_reduce_can_queue(struct fc_lport *lp)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ unsigned long flags;
+ int can_queue;
+
+ spin_lock_irqsave(lp->host->host_lock, flags);
+ if (si->throttled)
+ goto done;
+ si->throttled = 1;
+
+ can_queue = lp->host->can_queue;
+ can_queue >>= 1;
+ if (!can_queue)
+ can_queue = 1;
+ lp->host->can_queue = can_queue;
+ shost_printk(KERN_ERR, lp->host, "Could not allocate frame.\n"
+ "Reducing can_queue to %d.\n", can_queue);
+done:
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+}
+
+/*
+ * exch mgr calls this routine to process scsi
+ * exchanges.
+ *
+ * Return : None
+ * Context : called from Soft IRQ context
+ * can not called holding list lock
+ */
+static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+ struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg;
+ struct fc_lport *lp;
+ struct fc_frame_header *fh;
+ struct fcp_txrdy *dd;
+ u8 r_ctl;
+ int rc = 0;
+
+ if (IS_ERR(fp))
+ goto errout;
+
+ fh = fc_frame_header_get(fp);
+ r_ctl = fh->fh_r_ctl;
+ lp = fsp->lp;
+
+ if (!(lp->state & LPORT_ST_READY))
+ goto out;
+ if (fc_fcp_lock_pkt(fsp))
+ goto out;
+ fsp->last_pkt_time = jiffies;
+
+ if (fh->fh_type == FC_TYPE_BLS) {
+ fc_fcp_abts_resp(fsp, fp);
+ goto unlock;
+ }
+
+ if (fsp->state & (FC_SRB_ABORTED | FC_SRB_ABORT_PENDING))
+ goto unlock;
+
+ if (r_ctl == FC_RCTL_DD_DATA_DESC) {
+ /*
+ * received XFER RDY from the target
+ * need to send data to the target
+ */
+ WARN_ON(fr_flags(fp) & FCPHF_CRC_UNCHECKED);
+ dd = fc_frame_payload_get(fp, sizeof(*dd));
+ WARN_ON(!dd);
+
+ rc = fc_fcp_send_data(fsp, seq,
+ (size_t) ntohl(dd->ft_data_ro),
+ (size_t) ntohl(dd->ft_burst_len));
+ if (!rc)
+ seq->rec_data = fsp->xfer_len;
+ else if (rc == -ENOMEM)
+ fsp->state |= FC_SRB_NOMEM;
+ } else if (r_ctl == FC_RCTL_DD_SOL_DATA) {
+ /*
+ * received a DATA frame
+ * next we will copy the data to the system buffer
+ */
+ WARN_ON(fr_len(fp) < sizeof(*fh)); /* len may be 0 */
+ fc_fcp_recv_data(fsp, fp);
+ seq->rec_data = fsp->xfer_contig_end;
+ } else if (r_ctl == FC_RCTL_DD_CMD_STATUS) {
+ WARN_ON(fr_flags(fp) & FCPHF_CRC_UNCHECKED);
+
+ fc_fcp_resp(fsp, fp);
+ } else {
+ FC_DBG("unexpected frame. r_ctl %x\n", r_ctl);
+ }
+unlock:
+ fc_fcp_unlock_pkt(fsp);
+out:
+ fc_frame_free(fp);
+errout:
+ if (IS_ERR(fp))
+ fc_fcp_error(fsp, fp);
+ else if (rc == -ENOMEM)
+ fc_fcp_reduce_can_queue(lp);
+}
+
+static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
+{
+ struct fc_frame_header *fh;
+ struct fcp_resp *fc_rp;
+ struct fcp_resp_ext *rp_ex;
+ struct fcp_resp_rsp_info *fc_rp_info;
+ u32 plen;
+ u32 expected_len;
+ u32 respl = 0;
+ u32 snsl = 0;
+ u8 flags = 0;
+
+ plen = fr_len(fp);
+ fh = (struct fc_frame_header *)fr_hdr(fp);
+ if (unlikely(plen < sizeof(*fh) + sizeof(*fc_rp)))
+ goto len_err;
+ plen -= sizeof(*fh);
+ fc_rp = (struct fcp_resp *)(fh + 1);
+ fsp->cdb_status = fc_rp->fr_status;
+ flags = fc_rp->fr_flags;
+ fsp->scsi_comp_flags = flags;
+ expected_len = fsp->data_len;
+
+ if (unlikely((flags & ~FCP_CONF_REQ) || fc_rp->fr_status)) {
+ rp_ex = (void *)(fc_rp + 1);
+ if (flags & (FCP_RSP_LEN_VAL | FCP_SNS_LEN_VAL)) {
+ if (plen < sizeof(*fc_rp) + sizeof(*rp_ex))
+ goto len_err;
+ fc_rp_info = (struct fcp_resp_rsp_info *)(rp_ex + 1);
+ if (flags & FCP_RSP_LEN_VAL) {
+ respl = ntohl(rp_ex->fr_rsp_len);
+ if (respl != sizeof(*fc_rp_info))
+ goto len_err;
+ if (fsp->wait_for_comp) {
+ /* Abuse cdb_status for rsp code */
+ fsp->cdb_status = fc_rp_info->rsp_code;
+ complete(&fsp->tm_done);
+ /*
+ * tmfs will not have any scsi cmd so
+ * exit here
+ */
+ return;
+ } else
+ goto err;
+ }
+ if (flags & FCP_SNS_LEN_VAL) {
+ snsl = ntohl(rp_ex->fr_sns_len);
+ if (snsl > SCSI_SENSE_BUFFERSIZE)
+ snsl = SCSI_SENSE_BUFFERSIZE;
+ memcpy(fsp->cmd->sense_buffer,
+ (char *)fc_rp_info + respl, snsl);
+ }
+ }
+ if (flags & (FCP_RESID_UNDER | FCP_RESID_OVER)) {
+ if (plen < sizeof(*fc_rp) + sizeof(rp_ex->fr_resid))
+ goto len_err;
+ if (flags & FCP_RESID_UNDER) {
+ fsp->scsi_resid = ntohl(rp_ex->fr_resid);
+ /*
+ * The cmnd->underflow is the minimum number of
+ * bytes that must be transfered for this
+ * command. Provided a sense condition is not
+ * present, make sure the actual amount
+ * transferred is at least the underflow value
+ * or fail.
+ */
+ if (!(flags & FCP_SNS_LEN_VAL) &&
+ (fc_rp->fr_status == 0) &&
+ (scsi_bufflen(fsp->cmd) -
+ fsp->scsi_resid) < fsp->cmd->underflow)
+ goto err;
+ expected_len -= fsp->scsi_resid;
+ } else {
+ fsp->status_code = FC_ERROR;
+ }
+ }
+ }
+ fsp->state |= FC_SRB_RCV_STATUS;
+
+ /*
+ * Check for missing or extra data frames.
+ */
+ if (unlikely(fsp->xfer_len != expected_len)) {
+ if (fsp->xfer_len < expected_len) {
+ /*
+ * Some data may be queued locally,
+ * Wait a at least one jiffy to see if it is delivered.
+ * If this expires without data, we may do SRR.
+ */
+ fc_fcp_timer_set(fsp, 2);
+ return;
+ }
+ fsp->status_code = FC_DATA_OVRRUN;
+ FC_DBG("tgt %6x xfer len %zx greater than expected len %x. "
+ "data len %x\n",
+ fsp->rport->port_id,
+ fsp->xfer_len, expected_len, fsp->data_len);
+ }
+ fc_fcp_complete_locked(fsp);
+ return;
+
+len_err:
+ FC_DBG("short FCP response. flags 0x%x len %u respl %u snsl %u\n",
+ flags, fr_len(fp), respl, snsl);
+err:
+ fsp->status_code = FC_ERROR;
+ fc_fcp_complete_locked(fsp);
+}
+
+/**
+ * fc_fcp_complete_locked - complete processing of a fcp packet
+ * @fsp: fcp packet
+ *
+ * This function may sleep if a timer is pending. The packet lock must be
+ * held, and the host lock must not be held.
+ */
+static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
+{
+ struct fc_lport *lp = fsp->lp;
+ struct fc_seq *seq;
+ struct fc_exch *ep;
+ u32 f_ctl;
+
+ if (fsp->state & FC_SRB_ABORT_PENDING)
+ return;
+
+ if (fsp->state & FC_SRB_ABORTED) {
+ if (!fsp->status_code)
+ fsp->status_code = FC_CMD_ABORTED;
+ } else {
+ /*
+ * Test for transport underrun, independent of response
+ * underrun status.
+ */
+ if (fsp->xfer_len < fsp->data_len && !fsp->io_status &&
+ (!(fsp->scsi_comp_flags & FCP_RESID_UNDER) ||
+ fsp->xfer_len < fsp->data_len - fsp->scsi_resid)) {
+ fsp->status_code = FC_DATA_UNDRUN;
+ fsp->io_status = SUGGEST_RETRY << 24;
+ }
+ }
+
+ seq = fsp->seq_ptr;
+ if (seq) {
+ fsp->seq_ptr = NULL;
+ if (unlikely(fsp->scsi_comp_flags & FCP_CONF_REQ)) {
+ struct fc_frame *conf_frame;
+ struct fc_seq *csp;
+
+ csp = lp->tt.seq_start_next(seq);
+ conf_frame = fc_frame_alloc(fsp->lp, 0);
+ if (conf_frame) {
+ f_ctl = FC_FC_SEQ_INIT;
+ f_ctl |= FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+ ep = fc_seq_exch(seq);
+ fc_fill_fc_hdr(conf_frame, FC_RCTL_DD_SOL_CTL,
+ ep->did, ep->sid,
+ FC_TYPE_FCP, f_ctl, 0);
+ lp->tt.seq_send(lp, csp, conf_frame);
+ }
+ }
+ lp->tt.exch_done(seq);
+ }
+ fc_io_compl(fsp);
+}
+
+static void fc_fcp_cleanup_cmd(struct fc_fcp_pkt *fsp, int error)
+{
+ struct fc_lport *lp = fsp->lp;
+
+ if (fsp->seq_ptr) {
+ lp->tt.exch_done(fsp->seq_ptr);
+ fsp->seq_ptr = NULL;
+ }
+ fsp->status_code = error;
+}
+
+/**
+ * fc_fcp_cleanup_each_cmd - run fn on each active command
+ * @lp: logical port
+ * @id: target id
+ * @lun: lun
+ * @error: fsp status code
+ *
+ * If lun or id is -1, they are ignored.
+ */
+static void fc_fcp_cleanup_each_cmd(struct fc_lport *lp, unsigned int id,
+ unsigned int lun, int error)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ struct fc_fcp_pkt *fsp;
+ struct scsi_cmnd *sc_cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(lp->host->host_lock, flags);
+restart:
+ list_for_each_entry(fsp, &si->scsi_pkt_queue, list) {
+ sc_cmd = fsp->cmd;
+ if (id != -1 && scmd_id(sc_cmd) != id)
+ continue;
+
+ if (lun != -1 && sc_cmd->device->lun != lun)
+ continue;
+
+ fc_fcp_pkt_hold(fsp);
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+
+ if (!fc_fcp_lock_pkt(fsp)) {
+ fc_fcp_cleanup_cmd(fsp, error);
+ fc_io_compl(fsp);
+ fc_fcp_unlock_pkt(fsp);
+ }
+
+ fc_fcp_pkt_release(fsp);
+ spin_lock_irqsave(lp->host->host_lock, flags);
+ /*
+ * while we dropped the lock multiple pkts could
+ * have been released, so we have to start over.
+ */
+ goto restart;
+ }
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+}
+
+static void fc_fcp_abort_io(struct fc_lport *lp)
+{
+ fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_HRD_ERROR);
+}
+
+/**
+ * fc_fcp_pkt_send - send a fcp packet to the lower level.
+ * @lp: fc lport
+ * @fsp: fc packet.
+ *
+ * This is called by upper layer protocol.
+ * Return : zero for success and -1 for failure
+ * Context : called from queuecommand which can be called from process
+ * or scsi soft irq.
+ * Locks : called with the host lock and irqs disabled.
+ */
+static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ int rc;
+
+ fsp->cmd->SCp.ptr = (char *)fsp;
+ fsp->cdb_cmd.fc_dl = htonl(fsp->data_len);
+ fsp->cdb_cmd.fc_flags = fsp->req_flags & ~FCP_CFL_LEN_MASK;
+
+ int_to_scsilun(fsp->cmd->device->lun,
+ (struct scsi_lun *)fsp->cdb_cmd.fc_lun);
+ memcpy(fsp->cdb_cmd.fc_cdb, fsp->cmd->cmnd, fsp->cmd->cmd_len);
+ list_add_tail(&fsp->list, &si->scsi_pkt_queue);
+
+ spin_unlock_irq(lp->host->host_lock);
+ rc = lp->tt.fcp_cmd_send(lp, fsp, fc_fcp_recv);
+ spin_lock_irq(lp->host->host_lock);
+ if (rc)
+ list_del(&fsp->list);
+
+ return rc;
+}
+
+static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *fp,
+ void *arg))
+{
+ struct fc_frame *fp;
+ struct fc_seq *seq;
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rp;
+ const size_t len = sizeof(fsp->cdb_cmd);
+ int rc = 0;
+
+ if (fc_fcp_lock_pkt(fsp))
+ return 0;
+
+ fp = fc_frame_alloc(lp, sizeof(fsp->cdb_cmd));
+ if (!fp) {
+ rc = -1;
+ goto unlock;
+ }
+
+ memcpy(fc_frame_payload_get(fp, len), &fsp->cdb_cmd, len);
+ fr_cmd(fp) = fsp->cmd;
+ rport = fsp->rport;
+ fsp->max_payload = rport->maxframe_size;
+ rp = rport->dd_data;
+
+ fc_fill_fc_hdr(fp, FC_RCTL_DD_UNSOL_CMD, rport->port_id,
+ fc_host_port_id(rp->local_port->host), FC_TYPE_FCP,
+ FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+ seq = lp->tt.exch_seq_send(lp, fp, resp, fc_fcp_pkt_destroy, fsp, 0);
+ if (!seq) {
+ fc_frame_free(fp);
+ rc = -1;
+ goto unlock;
+ }
+ fsp->last_pkt_time = jiffies;
+ fsp->seq_ptr = seq;
+ fc_fcp_pkt_hold(fsp); /* hold for fc_fcp_pkt_destroy */
+
+ setup_timer(&fsp->timer, fc_fcp_timeout, (unsigned long)fsp);
+ fc_fcp_timer_set(fsp,
+ (fsp->tgt_flags & FC_RP_FLAGS_REC_SUPPORTED) ?
+ FC_SCSI_REC_TOV : FC_SCSI_ER_TIMEOUT);
+unlock:
+ fc_fcp_unlock_pkt(fsp);
+ return rc;
+}
+
+/*
+ * transport error handler
+ */
+static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
+{
+ int error = PTR_ERR(fp);
+
+ if (fc_fcp_lock_pkt(fsp))
+ return;
+
+ switch (error) {
+ case -FC_EX_CLOSED:
+ fc_fcp_retry_cmd(fsp);
+ goto unlock;
+ default:
+ FC_DBG("unknown error %ld\n", PTR_ERR(fp));
+ }
+ /*
+ * clear abort pending, because the lower layer
+ * decided to force completion.
+ */
+ fsp->state &= ~FC_SRB_ABORT_PENDING;
+ fsp->status_code = FC_CMD_PLOGO;
+ fc_fcp_complete_locked(fsp);
+unlock:
+ fc_fcp_unlock_pkt(fsp);
+}
+
+/*
+ * Scsi abort handler- calls to send an abort
+ * and then wait for abort completion
+ */
+static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
+{
+ int rc = FAILED;
+
+ if (fc_fcp_send_abort(fsp))
+ return FAILED;
+
+ init_completion(&fsp->tm_done);
+ fsp->wait_for_comp = 1;
+
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+ rc = wait_for_completion_timeout(&fsp->tm_done, FC_SCSI_TM_TOV);
+ spin_lock_bh(&fsp->scsi_pkt_lock);
+ fsp->wait_for_comp = 0;
+
+ if (!rc) {
+ FC_DBG("target abort cmd failed\n");
+ rc = FAILED;
+ } else if (fsp->state & FC_SRB_ABORTED) {
+ FC_DBG("target abort cmd passed\n");
+ rc = SUCCESS;
+ fc_fcp_complete_locked(fsp);
+ }
+
+ return rc;
+}
+
+/*
+ * Retry LUN reset after resource allocation failed.
+ */
+static void fc_lun_reset_send(unsigned long data)
+{
+ struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
+ struct fc_lport *lp = fsp->lp;
+ if (lp->tt.fcp_cmd_send(lp, fsp, fc_tm_done)) {
+ if (fsp->recov_retry++ >= FC_MAX_RECOV_RETRY)
+ return;
+ if (fc_fcp_lock_pkt(fsp))
+ return;
+ setup_timer(&fsp->timer, fc_lun_reset_send, (unsigned long)fsp);
+ fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+ fc_fcp_unlock_pkt(fsp);
+ }
+}
+
+/*
+ * Scsi device reset handler- send a LUN RESET to the device
+ * and wait for reset reply
+ */
+static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
+ unsigned int id, unsigned int lun)
+{
+ int rc;
+
+ fsp->cdb_cmd.fc_dl = htonl(fsp->data_len);
+ fsp->cdb_cmd.fc_tm_flags = FCP_TMF_LUN_RESET;
+ int_to_scsilun(lun, (struct scsi_lun *)fsp->cdb_cmd.fc_lun);
+
+ fsp->wait_for_comp = 1;
+ init_completion(&fsp->tm_done);
+
+ fc_lun_reset_send((unsigned long)fsp);
+
+ /*
+ * wait for completion of reset
+ * after that make sure all commands are terminated
+ */
+ rc = wait_for_completion_timeout(&fsp->tm_done, FC_SCSI_TM_TOV);
+
+ spin_lock_bh(&fsp->scsi_pkt_lock);
+ fsp->state |= FC_SRB_COMPL;
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+
+ del_timer_sync(&fsp->timer);
+
+ spin_lock_bh(&fsp->scsi_pkt_lock);
+ if (fsp->seq_ptr) {
+ lp->tt.exch_done(fsp->seq_ptr);
+ fsp->seq_ptr = NULL;
+ }
+ fsp->wait_for_comp = 0;
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+
+ if (!rc) {
+ FC_DBG("lun reset failed\n");
+ return FAILED;
+ }
+
+ /* cdb_status holds the tmf's rsp code */
+ if (fsp->cdb_status != FCP_TMF_CMPL)
+ return FAILED;
+
+ FC_DBG("lun reset to lun %u completed\n", lun);
+ fc_fcp_cleanup_each_cmd(lp, id, lun, FC_CMD_ABORTED);
+ return SUCCESS;
+}
+
+/*
+ * Task Managment response handler
+ */
+static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+ struct fc_fcp_pkt *fsp = arg;
+ struct fc_frame_header *fh;
+
+ if (IS_ERR(fp)) {
+ /*
+ * If there is an error just let it timeout or wait
+ * for TMF to be aborted if it timedout.
+ *
+ * scsi-eh will escalate for when either happens.
+ */
+ return;
+ }
+
+ if (fc_fcp_lock_pkt(fsp))
+ return;
+
+ /*
+ * raced with eh timeout handler.
+ */
+ if (!fsp->seq_ptr || !fsp->wait_for_comp) {
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+ return;
+ }
+
+ fh = fc_frame_header_get(fp);
+ if (fh->fh_type != FC_TYPE_BLS)
+ fc_fcp_resp(fsp, fp);
+ fsp->seq_ptr = NULL;
+ fsp->lp->tt.exch_done(seq);
+ fc_frame_free(fp);
+ fc_fcp_unlock_pkt(fsp);
+}
+
+static void fc_fcp_cleanup(struct fc_lport *lp)
+{
+ fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_ERROR);
+}
+
+/*
+ * fc_fcp_timeout: called by OS timer function.
+ *
+ * The timer has been inactivated and must be reactivated if desired
+ * using fc_fcp_timer_set().
+ *
+ * Algorithm:
+ *
+ * If REC is supported, just issue it, and return. The REC exchange will
+ * complete or time out, and recovery can continue at that point.
+ *
+ * Otherwise, if the response has been received without all the data,
+ * it has been ER_TIMEOUT since the response was received.
+ *
+ * If the response has not been received,
+ * we see if data was received recently. If it has been, we continue waiting,
+ * otherwise, we abort the command.
+ */
+static void fc_fcp_timeout(unsigned long data)
+{
+ struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
+ struct fc_rport *rport = fsp->rport;
+ struct fc_rport_libfc_priv *rp = rport->dd_data;
+
+ if (fc_fcp_lock_pkt(fsp))
+ return;
+
+ if (fsp->cdb_cmd.fc_tm_flags)
+ goto unlock;
+
+ fsp->state |= FC_SRB_FCP_PROCESSING_TMO;
+
+ if (rp->flags & FC_RP_FLAGS_REC_SUPPORTED)
+ fc_fcp_rec(fsp);
+ else if (time_after_eq(fsp->last_pkt_time + (FC_SCSI_ER_TIMEOUT / 2),
+ jiffies))
+ fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);
+ else if (fsp->state & FC_SRB_RCV_STATUS)
+ fc_fcp_complete_locked(fsp);
+ else
+ fc_timeout_error(fsp);
+ fsp->state &= ~FC_SRB_FCP_PROCESSING_TMO;
+unlock:
+ fc_fcp_unlock_pkt(fsp);
+}
+
+/*
+ * Send a REC ELS request
+ */
+static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
+{
+ struct fc_lport *lp;
+ struct fc_frame *fp;
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rp;
+
+ lp = fsp->lp;
+ rport = fsp->rport;
+ rp = rport->dd_data;
+ if (!fsp->seq_ptr || rp->rp_state != RPORT_ST_READY) {
+ fsp->status_code = FC_HRD_ERROR;
+ fsp->io_status = SUGGEST_RETRY << 24;
+ fc_fcp_complete_locked(fsp);
+ return;
+ }
+ fp = fc_frame_alloc(lp, sizeof(struct fc_els_rec));
+ if (!fp)
+ goto retry;
+
+ fr_seq(fp) = fsp->seq_ptr;
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rport->port_id,
+ fc_host_port_id(rp->local_port->host), FC_TYPE_ELS,
+ FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+ if (lp->tt.elsct_send(lp, rport, fp, ELS_REC, fc_fcp_rec_resp,
+ fsp, jiffies_to_msecs(FC_SCSI_REC_TOV))) {
+ fc_fcp_pkt_hold(fsp); /* hold while REC outstanding */
+ return;
+ }
+ fc_frame_free(fp);
+retry:
+ if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
+ fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+ else
+ fc_timeout_error(fsp);
+}
+
+/*
+ * Receive handler for REC ELS frame
+ * if it is a reject then let the scsi layer to handle
+ * the timeout. if it is a LS_ACC then if the io was not completed
+ * then set the timeout and return otherwise complete the exchange
+ * and tell the scsi layer to restart the I/O.
+ */
+static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+ struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg;
+ struct fc_els_rec_acc *recp;
+ struct fc_els_ls_rjt *rjt;
+ u32 e_stat;
+ u8 opcode;
+ u32 offset;
+ enum dma_data_direction data_dir;
+ enum fc_rctl r_ctl;
+ struct fc_rport_libfc_priv *rp;
+
+ if (IS_ERR(fp)) {
+ fc_fcp_rec_error(fsp, fp);
+ return;
+ }
+
+ if (fc_fcp_lock_pkt(fsp))
+ goto out;
+
+ fsp->recov_retry = 0;
+ opcode = fc_frame_payload_op(fp);
+ if (opcode == ELS_LS_RJT) {
+ rjt = fc_frame_payload_get(fp, sizeof(*rjt));
+ switch (rjt->er_reason) {
+ default:
+ FC_DEBUG_FCP("device %x unexpected REC reject "
+ "reason %d expl %d\n",
+ fsp->rport->port_id, rjt->er_reason,
+ rjt->er_explan);
+ /* fall through */
+ case ELS_RJT_UNSUP:
+ FC_DEBUG_FCP("device does not support REC\n");
+ rp = fsp->rport->dd_data;
+ /*
+ * if we do not spport RECs or got some bogus
+ * reason then resetup timer so we check for
+ * making progress.
+ */
+ rp->flags &= ~FC_RP_FLAGS_REC_SUPPORTED;
+ fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);
+ break;
+ case ELS_RJT_LOGIC:
+ case ELS_RJT_UNAB:
+ /*
+ * If no data transfer, the command frame got dropped
+ * so we just retry. If data was transferred, we
+ * lost the response but the target has no record,
+ * so we abort and retry.
+ */
+ if (rjt->er_explan == ELS_EXPL_OXID_RXID &&
+ fsp->xfer_len == 0) {
+ fc_fcp_retry_cmd(fsp);
+ break;
+ }
+ fc_timeout_error(fsp);
+ break;
+ }
+ } else if (opcode == ELS_LS_ACC) {
+ if (fsp->state & FC_SRB_ABORTED)
+ goto unlock_out;
+
+ data_dir = fsp->cmd->sc_data_direction;
+ recp = fc_frame_payload_get(fp, sizeof(*recp));
+ offset = ntohl(recp->reca_fc4value);
+ e_stat = ntohl(recp->reca_e_stat);
+
+ if (e_stat & ESB_ST_COMPLETE) {
+
+ /*
+ * The exchange is complete.
+ *
+ * For output, we must've lost the response.
+ * For input, all data must've been sent.
+ * We lost may have lost the response
+ * (and a confirmation was requested) and maybe
+ * some data.
+ *
+ * If all data received, send SRR
+ * asking for response. If partial data received,
+ * or gaps, SRR requests data at start of gap.
+ * Recovery via SRR relies on in-order-delivery.
+ */
+ if (data_dir == DMA_TO_DEVICE) {
+ r_ctl = FC_RCTL_DD_CMD_STATUS;
+ } else if (fsp->xfer_contig_end == offset) {
+ r_ctl = FC_RCTL_DD_CMD_STATUS;
+ } else {
+ offset = fsp->xfer_contig_end;
+ r_ctl = FC_RCTL_DD_SOL_DATA;
+ }
+ fc_fcp_srr(fsp, r_ctl, offset);
+ } else if (e_stat & ESB_ST_SEQ_INIT) {
+
+ /*
+ * The remote port has the initiative, so just
+ * keep waiting for it to complete.
+ */
+ fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+ } else {
+
+ /*
+ * The exchange is incomplete, we have seq. initiative.
+ * Lost response with requested confirmation,
+ * lost confirmation, lost transfer ready or
+ * lost write data.
+ *
+ * For output, if not all data was received, ask
+ * for transfer ready to be repeated.
+ *
+ * If we received or sent all the data, send SRR to
+ * request response.
+ *
+ * If we lost a response, we may have lost some read
+ * data as well.
+ */
+ r_ctl = FC_RCTL_DD_SOL_DATA;
+ if (data_dir == DMA_TO_DEVICE) {
+ r_ctl = FC_RCTL_DD_CMD_STATUS;
+ if (offset < fsp->data_len)
+ r_ctl = FC_RCTL_DD_DATA_DESC;
+ } else if (offset == fsp->xfer_contig_end) {
+ r_ctl = FC_RCTL_DD_CMD_STATUS;
+ } else if (fsp->xfer_contig_end < offset) {
+ offset = fsp->xfer_contig_end;
+ }
+ fc_fcp_srr(fsp, r_ctl, offset);
+ }
+ }
+unlock_out:
+ fc_fcp_unlock_pkt(fsp);
+out:
+ fc_fcp_pkt_release(fsp); /* drop hold for outstanding REC */
+ fc_frame_free(fp);
+}
+
+/*
+ * Handle error response or timeout for REC exchange.
+ */
+static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
+{
+ int error = PTR_ERR(fp);
+
+ if (fc_fcp_lock_pkt(fsp))
+ goto out;
+
+ switch (error) {
+ case -FC_EX_CLOSED:
+ fc_fcp_retry_cmd(fsp);
+ break;
+
+ default:
+ FC_DBG("REC %p fid %x error unexpected error %d\n",
+ fsp, fsp->rport->port_id, error);
+ fsp->status_code = FC_CMD_PLOGO;
+ /* fall through */
+
+ case -FC_EX_TIMEOUT:
+ /*
+ * Assume REC or LS_ACC was lost.
+ * The exchange manager will have aborted REC, so retry.
+ */
+ FC_DBG("REC fid %x error error %d retry %d/%d\n",
+ fsp->rport->port_id, error, fsp->recov_retry,
+ FC_MAX_RECOV_RETRY);
+ if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
+ fc_fcp_rec(fsp);
+ else
+ fc_timeout_error(fsp);
+ break;
+ }
+ fc_fcp_unlock_pkt(fsp);
+out:
+ fc_fcp_pkt_release(fsp); /* drop hold for outstanding REC */
+}
+
+/*
+ * Time out error routine:
+ * abort's the I/O close the exchange and
+ * send completion notification to scsi layer
+ */
+static void fc_timeout_error(struct fc_fcp_pkt *fsp)
+{
+ fsp->status_code = FC_CMD_TIME_OUT;
+ fsp->cdb_status = 0;
+ fsp->io_status = 0;
+ /*
+ * if this fails then we let the scsi command timer fire and
+ * scsi-ml escalate.
+ */
+ fc_fcp_send_abort(fsp);
+}
+
+/*
+ * Sequence retransmission request.
+ * This is called after receiving status but insufficient data, or
+ * when expecting status but the request has timed out.
+ */
+static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
+{
+ struct fc_lport *lp = fsp->lp;
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rp;
+ struct fc_exch *ep = fc_seq_exch(fsp->seq_ptr);
+ struct fc_seq *seq;
+ struct fcp_srr *srr;
+ struct fc_frame *fp;
+ u8 cdb_op;
+
+ rport = fsp->rport;
+ rp = rport->dd_data;
+ cdb_op = fsp->cdb_cmd.fc_cdb[0];
+
+ if (!(rp->flags & FC_RP_FLAGS_RETRY) || rp->rp_state != RPORT_ST_READY)
+ goto retry; /* shouldn't happen */
+ fp = fc_frame_alloc(lp, sizeof(*srr));
+ if (!fp)
+ goto retry;
+
+ srr = fc_frame_payload_get(fp, sizeof(*srr));
+ memset(srr, 0, sizeof(*srr));
+ srr->srr_op = ELS_SRR;
+ srr->srr_ox_id = htons(ep->oxid);
+ srr->srr_rx_id = htons(ep->rxid);
+ srr->srr_r_ctl = r_ctl;
+ srr->srr_rel_off = htonl(offset);
+
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS4_REQ, rport->port_id,
+ fc_host_port_id(rp->local_port->host), FC_TYPE_FCP,
+ FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+ seq = lp->tt.exch_seq_send(lp, fp, fc_fcp_srr_resp, NULL,
+ fsp, jiffies_to_msecs(FC_SCSI_REC_TOV));
+ if (!seq) {
+ fc_frame_free(fp);
+ goto retry;
+ }
+ fsp->recov_seq = seq;
+ fsp->xfer_len = offset;
+ fsp->xfer_contig_end = offset;
+ fsp->state &= ~FC_SRB_RCV_STATUS;
+ fc_fcp_pkt_hold(fsp); /* hold for outstanding SRR */
+ return;
+retry:
+ fc_fcp_retry_cmd(fsp);
+}
+
+/*
+ * Handle response from SRR.
+ */
+static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+ struct fc_fcp_pkt *fsp = arg;
+ struct fc_frame_header *fh;
+
+ if (IS_ERR(fp)) {
+ fc_fcp_srr_error(fsp, fp);
+ return;
+ }
+
+ if (fc_fcp_lock_pkt(fsp))
+ goto out;
+
+ fh = fc_frame_header_get(fp);
+ /*
+ * BUG? fc_fcp_srr_error calls exch_done which would release
+ * the ep. But if fc_fcp_srr_error had got -FC_EX_TIMEOUT,
+ * then fc_exch_timeout would be sending an abort. The exch_done
+ * call by fc_fcp_srr_error would prevent fc_exch.c from seeing
+ * an abort response though.
+ */
+ if (fh->fh_type == FC_TYPE_BLS) {
+ fc_fcp_unlock_pkt(fsp);
+ return;
+ }
+
+ fsp->recov_seq = NULL;
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_LS_ACC:
+ fsp->recov_retry = 0;
+ fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+ break;
+ case ELS_LS_RJT:
+ default:
+ fc_timeout_error(fsp);
+ break;
+ }
+ fc_fcp_unlock_pkt(fsp);
+ fsp->lp->tt.exch_done(seq);
+out:
+ fc_frame_free(fp);
+ fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */
+}
+
+static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
+{
+ if (fc_fcp_lock_pkt(fsp))
+ goto out;
+ fsp->lp->tt.exch_done(fsp->recov_seq);
+ fsp->recov_seq = NULL;
+ switch (PTR_ERR(fp)) {
+ case -FC_EX_TIMEOUT:
+ if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
+ fc_fcp_rec(fsp);
+ else
+ fc_timeout_error(fsp);
+ break;
+ case -FC_EX_CLOSED: /* e.g., link failure */
+ /* fall through */
+ default:
+ fc_fcp_retry_cmd(fsp);
+ break;
+ }
+ fc_fcp_unlock_pkt(fsp);
+out:
+ fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */
+}
+
+static inline int fc_fcp_lport_queue_ready(struct fc_lport *lp)
+{
+ /* lock ? */
+ return (lp->state == LPORT_ST_READY) && (lp->link_status & FC_LINK_UP);
+}
+
+/**
+ * fc_queuecommand - The queuecommand function of the scsi template
+ * @cmd: struct scsi_cmnd to be executed
+ * @done: Callback function to be called when cmd is completed
+ *
+ * this is the i/o strategy routine, called by the scsi layer
+ * this routine is called with holding the host_lock.
+ */
+int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
+{
+ struct fc_lport *lp;
+ struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
+ struct fc_fcp_pkt *fsp;
+ struct fc_rport_libfc_priv *rp;
+ int rval;
+ int rc = 0;
+ struct fcoe_dev_stats *stats;
+
+ lp = shost_priv(sc_cmd->device->host);
+
+ rval = fc_remote_port_chkready(rport);
+ if (rval) {
+ sc_cmd->result = rval;
+ done(sc_cmd);
+ goto out;
+ }
+
+ if (!*(struct fc_remote_port **)rport->dd_data) {
+ /*
+ * rport is transitioning from blocked/deleted to
+ * online
+ */
+ sc_cmd->result = DID_IMM_RETRY << 16;
+ done(sc_cmd);
+ goto out;
+ }
+
+ rp = rport->dd_data;
+
+ if (!fc_fcp_lport_queue_ready(lp)) {
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+
+ fsp = fc_fcp_pkt_alloc(lp, GFP_ATOMIC);
+ if (fsp == NULL) {
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+
+ /*
+ * build the libfc request pkt
+ */
+ fsp->cmd = sc_cmd; /* save the cmd */
+ fsp->lp = lp; /* save the softc ptr */
+ fsp->rport = rport; /* set the remote port ptr */
+ sc_cmd->scsi_done = done;
+
+ /*
+ * set up the transfer length
+ */
+ fsp->data_len = scsi_bufflen(sc_cmd);
+ fsp->xfer_len = 0;
+
+ /*
+ * setup the data direction
+ */
+ stats = lp->dev_stats[smp_processor_id()];
+ if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ fsp->req_flags = FC_SRB_READ;
+ stats->InputRequests++;
+ stats->InputMegabytes = fsp->data_len;
+ } else if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {
+ fsp->req_flags = FC_SRB_WRITE;
+ stats->OutputRequests++;
+ stats->OutputMegabytes = fsp->data_len;
+ } else {
+ fsp->req_flags = 0;
+ stats->ControlRequests++;
+ }
+
+ fsp->tgt_flags = rp->flags;
+
+ init_timer(&fsp->timer);
+ fsp->timer.data = (unsigned long)fsp;
+
+ /*
+ * send it to the lower layer
+ * if we get -1 return then put the request in the pending
+ * queue.
+ */
+ rval = fc_fcp_pkt_send(lp, fsp);
+ if (rval != 0) {
+ fsp->state = FC_SRB_FREE;
+ fc_fcp_pkt_release(fsp);
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ }
+out:
+ return rc;
+}
+EXPORT_SYMBOL(fc_queuecommand);
+
+/**
+ * fc_io_compl - Handle responses for completed commands
+ * @fsp: scsi packet
+ *
+ * Translates a error to a Linux SCSI error.
+ *
+ * The fcp packet lock must be held when calling.
+ */
+static void fc_io_compl(struct fc_fcp_pkt *fsp)
+{
+ struct fc_fcp_internal *si;
+ struct scsi_cmnd *sc_cmd;
+ struct fc_lport *lp;
+ unsigned long flags;
+
+ fsp->state |= FC_SRB_COMPL;
+ if (!(fsp->state & FC_SRB_FCP_PROCESSING_TMO)) {
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+ del_timer_sync(&fsp->timer);
+ spin_lock_bh(&fsp->scsi_pkt_lock);
+ }
+
+ lp = fsp->lp;
+ si = fc_get_scsi_internal(lp);
+ spin_lock_irqsave(lp->host->host_lock, flags);
+ if (!fsp->cmd) {
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+ return;
+ }
+
+ /*
+ * if a command timed out while we had to try and throttle IO
+ * and it is now getting cleaned up, then we are about to
+ * try again so clear the throttled flag incase we get more
+ * time outs.
+ */
+ if (si->throttled && fsp->state & FC_SRB_NOMEM)
+ si->throttled = 0;
+
+ sc_cmd = fsp->cmd;
+ fsp->cmd = NULL;
+
+ if (!sc_cmd->SCp.ptr) {
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+ return;
+ }
+
+ CMD_SCSI_STATUS(sc_cmd) = fsp->cdb_status;
+ switch (fsp->status_code) {
+ case FC_COMPLETE:
+ if (fsp->cdb_status == 0) {
+ /*
+ * good I/O status
+ */
+ sc_cmd->result = DID_OK << 16;
+ if (fsp->scsi_resid)
+ CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid;
+ } else if (fsp->cdb_status == QUEUE_FULL) {
+ struct scsi_device *tmp_sdev;
+ struct scsi_device *sdev = sc_cmd->device;
+
+ shost_for_each_device(tmp_sdev, sdev->host) {
+ if (tmp_sdev->id != sdev->id)
+ continue;
+
+ if (tmp_sdev->queue_depth > 1) {
+ scsi_track_queue_full(tmp_sdev,
+ tmp_sdev->
+ queue_depth - 1);
+ }
+ }
+ sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
+ } else {
+ /*
+ * transport level I/O was ok but scsi
+ * has non zero status
+ */
+ sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
+ }
+ break;
+ case FC_ERROR:
+ sc_cmd->result = DID_ERROR << 16;
+ break;
+ case FC_DATA_UNDRUN:
+ if (fsp->cdb_status == 0) {
+ /*
+ * scsi status is good but transport level
+ * underrun. for read it should be an error??
+ */
+ sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
+ } else {
+ /*
+ * scsi got underrun, this is an error
+ */
+ CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid;
+ sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status;
+ }
+ break;
+ case FC_DATA_OVRRUN:
+ /*
+ * overrun is an error
+ */
+ sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status;
+ break;
+ case FC_CMD_ABORTED:
+ sc_cmd->result = (DID_ABORT << 16) | fsp->io_status;
+ break;
+ case FC_CMD_TIME_OUT:
+ sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status;
+ break;
+ case FC_CMD_RESET:
+ sc_cmd->result = (DID_RESET << 16);
+ break;
+ case FC_HRD_ERROR:
+ sc_cmd->result = (DID_NO_CONNECT << 16);
+ break;
+ default:
+ sc_cmd->result = (DID_ERROR << 16);
+ break;
+ }
+
+ list_del(&fsp->list);
+ sc_cmd->SCp.ptr = NULL;
+ sc_cmd->scsi_done(sc_cmd);
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+
+ /* release ref from initial allocation in queue command */
+ fc_fcp_pkt_release(fsp);
+}
+
+/**
+ * fc_fcp_complete - complete processing of a fcp packet
+ * @fsp: fcp packet
+ *
+ * This function may sleep if a fsp timer is pending.
+ * The host lock must not be held by caller.
+ */
+void fc_fcp_complete(struct fc_fcp_pkt *fsp)
+{
+ if (fc_fcp_lock_pkt(fsp))
+ return;
+
+ fc_fcp_complete_locked(fsp);
+ fc_fcp_unlock_pkt(fsp);
+}
+EXPORT_SYMBOL(fc_fcp_complete);
+
+/**
+ * fc_eh_abort - Abort a command...from scsi host template
+ * @sc_cmd: scsi command to abort
+ *
+ * send ABTS to the target device and wait for the response
+ * sc_cmd is the pointer to the command to be aborted.
+ */
+int fc_eh_abort(struct scsi_cmnd *sc_cmd)
+{
+ struct fc_fcp_pkt *fsp;
+ struct fc_lport *lp;
+ int rc = FAILED;
+ unsigned long flags;
+
+ lp = shost_priv(sc_cmd->device->host);
+ if (lp->state != LPORT_ST_READY)
+ return rc;
+ else if (!(lp->link_status & FC_LINK_UP))
+ return rc;
+
+ spin_lock_irqsave(lp->host->host_lock, flags);
+ fsp = CMD_SP(sc_cmd);
+ if (!fsp) {
+ /* command completed while scsi eh was setting up */
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+ return SUCCESS;
+ }
+ /* grab a ref so the fsp and sc_cmd cannot be relased from under us */
+ fc_fcp_pkt_hold(fsp);
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+
+ if (fc_fcp_lock_pkt(fsp)) {
+ /* completed while we were waiting for timer to be deleted */
+ rc = SUCCESS;
+ goto release_pkt;
+ }
+
+ rc = fc_fcp_pkt_abort(lp, fsp);
+ fc_fcp_unlock_pkt(fsp);
+
+release_pkt:
+ fc_fcp_pkt_release(fsp);
+ return rc;
+}
+EXPORT_SYMBOL(fc_eh_abort);
+
+/**
+ * fc_eh_device_reset: Reset a single LUN
+ * @sc_cmd: scsi command
+ *
+ * Set from scsi host template to send tm cmd to the target and wait for the
+ * response.
+ */
+int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
+{
+ struct fc_lport *lp;
+ struct fc_fcp_pkt *fsp;
+ struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
+ int rc = FAILED;
+ struct fc_rport_libfc_priv *rp;
+ int rval;
+
+ rval = fc_remote_port_chkready(rport);
+ if (rval)
+ goto out;
+
+ rp = rport->dd_data;
+ lp = shost_priv(sc_cmd->device->host);
+
+ if (lp->state != LPORT_ST_READY)
+ return rc;
+
+ fsp = fc_fcp_pkt_alloc(lp, GFP_NOIO);
+ if (fsp == NULL) {
+ FC_DBG("could not allocate scsi_pkt\n");
+ sc_cmd->result = DID_NO_CONNECT << 16;
+ goto out;
+ }
+
+ /*
+ * Build the libfc request pkt. Do not set the scsi cmnd, because
+ * the sc passed in is not setup for execution like when sent
+ * through the queuecommand callout.
+ */
+ fsp->lp = lp; /* save the softc ptr */
+ fsp->rport = rport; /* set the remote port ptr */
+
+ /*
+ * flush outstanding commands
+ */
+ rc = fc_lun_reset(lp, fsp, scmd_id(sc_cmd), sc_cmd->device->lun);
+ fsp->state = FC_SRB_FREE;
+ fc_fcp_pkt_release(fsp);
+
+out:
+ return rc;
+}
+EXPORT_SYMBOL(fc_eh_device_reset);
+
+/**
+ * fc_eh_host_reset - The reset function will reset the ports on the host.
+ * @sc_cmd: scsi command
+ */
+int fc_eh_host_reset(struct scsi_cmnd *sc_cmd)
+{
+ struct Scsi_Host *shost = sc_cmd->device->host;
+ struct fc_lport *lp = shost_priv(shost);
+ unsigned long wait_tmo;
+
+ lp->tt.lport_reset(lp);
+ wait_tmo = jiffies + FC_HOST_RESET_TIMEOUT;
+ while (!fc_fcp_lport_queue_ready(lp) && time_before(jiffies, wait_tmo))
+ msleep(1000);
+
+ if (fc_fcp_lport_queue_ready(lp)) {
+ shost_printk(KERN_INFO, shost, "Host reset succeeded.\n");
+ return SUCCESS;
+ } else {
+ shost_printk(KERN_INFO, shost, "Host reset failed. "
+ "lport not ready.\n");
+ return FAILED;
+ }
+}
+EXPORT_SYMBOL(fc_eh_host_reset);
+
+/**
+ * fc_slave_alloc - configure queue depth
+ * @sdev: scsi device
+ *
+ * Configures queue depth based on host's cmd_per_len. If not set
+ * then we use the libfc default.
+ */
+int fc_slave_alloc(struct scsi_device *sdev)
+{
+ struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+ int queue_depth;
+
+ if (!rport || fc_remote_port_chkready(rport))
+ return -ENXIO;
+
+ if (sdev->tagged_supported) {
+ if (sdev->host->hostt->cmd_per_lun)
+ queue_depth = sdev->host->hostt->cmd_per_lun;
+ else
+ queue_depth = FC_FCP_DFLT_QUEUE_DEPTH;
+ scsi_activate_tcq(sdev, queue_depth);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(fc_slave_alloc);
+
+int fc_change_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ return sdev->queue_depth;
+}
+EXPORT_SYMBOL(fc_change_queue_depth);
+
+int fc_change_queue_type(struct scsi_device *sdev, int tag_type)
+{
+ if (sdev->tagged_supported) {
+ scsi_set_tag_type(sdev, tag_type);
+ if (tag_type)
+ scsi_activate_tcq(sdev, sdev->queue_depth);
+ else
+ scsi_deactivate_tcq(sdev, sdev->queue_depth);
+ } else
+ tag_type = 0;
+
+ return tag_type;
+}
+EXPORT_SYMBOL(fc_change_queue_type);
+
+void fc_fcp_destroy(struct fc_lport *lp)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+
+ if (!list_empty(&si->scsi_pkt_queue))
+ printk(KERN_ERR "Leaked scsi packets.\n");
+
+ mempool_destroy(si->scsi_pkt_pool);
+ kfree(si);
+ lp->scsi_priv = NULL;
+}
+EXPORT_SYMBOL(fc_fcp_destroy);
+
+int fc_fcp_init(struct fc_lport *lp)
+{
+ int rc;
+ struct fc_fcp_internal *si;
+
+ if (!lp->tt.fcp_cmd_send)
+ lp->tt.fcp_cmd_send = fc_fcp_cmd_send;
+
+ if (!lp->tt.fcp_cleanup)
+ lp->tt.fcp_cleanup = fc_fcp_cleanup;
+
+ if (!lp->tt.fcp_abort_io)
+ lp->tt.fcp_abort_io = fc_fcp_abort_io;
+
+ si = kzalloc(sizeof(struct fc_fcp_internal), GFP_KERNEL);
+ if (!si)
+ return -ENOMEM;
+ lp->scsi_priv = si;
+ INIT_LIST_HEAD(&si->scsi_pkt_queue);
+
+ si->scsi_pkt_pool = mempool_create_slab_pool(2, scsi_pkt_cachep);
+ if (!si->scsi_pkt_pool) {
+ rc = -ENOMEM;
+ goto free_internal;
+ }
+ return 0;
+
+free_internal:
+ kfree(si);
+ return rc;
+}
+EXPORT_SYMBOL(fc_fcp_init);
+
+static int __init libfc_init(void)
+{
+ int rc;
+
+ scsi_pkt_cachep = kmem_cache_create("libfc_fcp_pkt",
+ sizeof(struct fc_fcp_pkt),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (scsi_pkt_cachep == NULL) {
+ FC_DBG("Unable to allocate SRB cache...module load failed!");
+ return -ENOMEM;
+ }
+
+ rc = fc_setup_exch_mgr();
+ if (rc)
+ goto destroy_pkt_cache;
+
+ rc = fc_setup_rport();
+ if (rc)
+ goto destroy_em;
+
+ return rc;
+destroy_em:
+ fc_destroy_exch_mgr();
+destroy_pkt_cache:
+ kmem_cache_destroy(scsi_pkt_cachep);
+ return rc;
+}
+
+static void __exit libfc_exit(void)
+{
+ kmem_cache_destroy(scsi_pkt_cachep);
+ fc_destroy_exch_mgr();
+ fc_destroy_rport();
+}
+
+module_init(libfc_init);
+module_exit(libfc_exit);
diff --git a/drivers/scsi/libfc/fc_frame.c b/drivers/scsi/libfc/fc_frame.c
new file mode 100644
index 000000000000..63fe00cfe667
--- /dev/null
+++ b/drivers/scsi/libfc/fc_frame.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * Frame allocation.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/crc32.h>
+
+#include <scsi/fc_frame.h>
+
+/*
+ * Check the CRC in a frame.
+ */
+u32 fc_frame_crc_check(struct fc_frame *fp)
+{
+ u32 crc;
+ u32 error;
+ const u8 *bp;
+ unsigned int len;
+
+ WARN_ON(!fc_frame_is_linear(fp));
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ len = (fr_len(fp) + 3) & ~3; /* round up length to include fill */
+ bp = (const u8 *) fr_hdr(fp);
+ crc = ~crc32(~0, bp, len);
+ error = crc ^ fr_crc(fp);
+ return error;
+}
+EXPORT_SYMBOL(fc_frame_crc_check);
+
+/*
+ * Allocate a frame intended to be sent via fcoe_xmit.
+ * Get an sk_buff for the frame and set the length.
+ */
+struct fc_frame *__fc_frame_alloc(size_t len)
+{
+ struct fc_frame *fp;
+ struct sk_buff *skb;
+
+ WARN_ON((len % sizeof(u32)) != 0);
+ len += sizeof(struct fc_frame_header);
+ skb = dev_alloc_skb(len + FC_FRAME_HEADROOM + FC_FRAME_TAILROOM);
+ if (!skb)
+ return NULL;
+ fp = (struct fc_frame *) skb;
+ fc_frame_init(fp);
+ skb_reserve(skb, FC_FRAME_HEADROOM);
+ skb_put(skb, len);
+ return fp;
+}
+EXPORT_SYMBOL(__fc_frame_alloc);
+
+
+struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
+{
+ struct fc_frame *fp;
+ size_t fill;
+
+ fill = payload_len % 4;
+ if (fill != 0)
+ fill = 4 - fill;
+ fp = __fc_frame_alloc(payload_len + fill);
+ if (fp) {
+ memset((char *) fr_hdr(fp) + payload_len, 0, fill);
+ /* trim is OK, we just allocated it so there are no fragments */
+ skb_trim(fp_skb(fp),
+ payload_len + sizeof(struct fc_frame_header));
+ }
+ return fp;
+}
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
new file mode 100644
index 000000000000..0b9bdb1fb807
--- /dev/null
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -0,0 +1,1604 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * PORT LOCKING NOTES
+ *
+ * These comments only apply to the 'port code' which consists of the lport,
+ * disc and rport blocks.
+ *
+ * MOTIVATION
+ *
+ * The lport, disc and rport blocks all have mutexes that are used to protect
+ * those objects. The main motivation for these locks is to prevent from
+ * having an lport reset just before we send a frame. In that scenario the
+ * lport's FID would get set to zero and then we'd send a frame with an
+ * invalid SID. We also need to ensure that states don't change unexpectedly
+ * while processing another state.
+ *
+ * HEIRARCHY
+ *
+ * The following heirarchy defines the locking rules. A greater lock
+ * may be held before acquiring a lesser lock, but a lesser lock should never
+ * be held while attempting to acquire a greater lock. Here is the heirarchy-
+ *
+ * lport > disc, lport > rport, disc > rport
+ *
+ * CALLBACKS
+ *
+ * The callbacks cause complications with this scheme. There is a callback
+ * from the rport (to either lport or disc) and a callback from disc
+ * (to the lport).
+ *
+ * As rports exit the rport state machine a callback is made to the owner of
+ * the rport to notify success or failure. Since the callback is likely to
+ * cause the lport or disc to grab its lock we cannot hold the rport lock
+ * while making the callback. To ensure that the rport is not free'd while
+ * processing the callback the rport callbacks are serialized through a
+ * single-threaded workqueue. An rport would never be free'd while in a
+ * callback handler becuase no other rport work in this queue can be executed
+ * at the same time.
+ *
+ * When discovery succeeds or fails a callback is made to the lport as
+ * notification. Currently, succesful discovery causes the lport to take no
+ * action. A failure will cause the lport to reset. There is likely a circular
+ * locking problem with this implementation.
+ */
+
+/*
+ * LPORT LOCKING
+ *
+ * The critical sections protected by the lport's mutex are quite broad and
+ * may be improved upon in the future. The lport code and its locking doesn't
+ * influence the I/O path, so excessive locking doesn't penalize I/O
+ * performance.
+ *
+ * The strategy is to lock whenever processing a request or response. Note
+ * that every _enter_* function corresponds to a state change. They generally
+ * change the lports state and then send a request out on the wire. We lock
+ * before calling any of these functions to protect that state change. This
+ * means that the entry points into the lport block manage the locks while
+ * the state machine can transition between states (i.e. _enter_* functions)
+ * while always staying protected.
+ *
+ * When handling responses we also hold the lport mutex broadly. When the
+ * lport receives the response frame it locks the mutex and then calls the
+ * appropriate handler for the particuar response. Generally a response will
+ * trigger a state change and so the lock must already be held.
+ *
+ * Retries also have to consider the locking. The retries occur from a work
+ * context and the work function will lock the lport and then retry the state
+ * (i.e. _enter_* function).
+ */
+
+#include <linux/timer.h>
+#include <asm/unaligned.h>
+
+#include <scsi/fc/fc_gs.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+/* Fabric IDs to use for point-to-point mode, chosen on whims. */
+#define FC_LOCAL_PTP_FID_LO 0x010101
+#define FC_LOCAL_PTP_FID_HI 0x010102
+
+#define DNS_DELAY 3 /* Discovery delay after RSCN (in seconds)*/
+
+static int fc_lport_debug;
+
+#define FC_DEBUG_LPORT(fmt...) \
+ do { \
+ if (fc_lport_debug) \
+ FC_DBG(fmt); \
+ } while (0)
+
+static void fc_lport_error(struct fc_lport *, struct fc_frame *);
+
+static void fc_lport_enter_reset(struct fc_lport *);
+static void fc_lport_enter_flogi(struct fc_lport *);
+static void fc_lport_enter_dns(struct fc_lport *);
+static void fc_lport_enter_rpn_id(struct fc_lport *);
+static void fc_lport_enter_rft_id(struct fc_lport *);
+static void fc_lport_enter_scr(struct fc_lport *);
+static void fc_lport_enter_ready(struct fc_lport *);
+static void fc_lport_enter_logo(struct fc_lport *);
+
+static const char *fc_lport_state_names[] = {
+ [LPORT_ST_NONE] = "none",
+ [LPORT_ST_FLOGI] = "FLOGI",
+ [LPORT_ST_DNS] = "dNS",
+ [LPORT_ST_RPN_ID] = "RPN_ID",
+ [LPORT_ST_RFT_ID] = "RFT_ID",
+ [LPORT_ST_SCR] = "SCR",
+ [LPORT_ST_READY] = "Ready",
+ [LPORT_ST_LOGO] = "LOGO",
+ [LPORT_ST_RESET] = "reset",
+};
+
+static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)
+{
+ fc_frame_free(fp);
+ return 0;
+}
+
+/**
+ * fc_lport_rport_callback - Event handler for rport events
+ * @lport: The lport which is receiving the event
+ * @rport: The rport which the event has occured on
+ * @event: The event that occured
+ *
+ * Locking Note: The rport lock should not be held when calling
+ * this function.
+ */
+static void fc_lport_rport_callback(struct fc_lport *lport,
+ struct fc_rport *rport,
+ enum fc_rport_event event)
+{
+ FC_DEBUG_LPORT("Received a %d event for port (%6x)\n", event,
+ rport->port_id);
+
+ switch (event) {
+ case RPORT_EV_CREATED:
+ if (rport->port_id == FC_FID_DIR_SERV) {
+ mutex_lock(&lport->lp_mutex);
+ if (lport->state == LPORT_ST_DNS) {
+ lport->dns_rp = rport;
+ fc_lport_enter_rpn_id(lport);
+ } else {
+ FC_DEBUG_LPORT("Received an CREATED event on "
+ "port (%6x) for the directory "
+ "server, but the lport is not "
+ "in the DNS state, it's in the "
+ "%d state", rport->port_id,
+ lport->state);
+ lport->tt.rport_logoff(rport);
+ }
+ mutex_unlock(&lport->lp_mutex);
+ } else
+ FC_DEBUG_LPORT("Received an event for port (%6x) "
+ "which is not the directory server\n",
+ rport->port_id);
+ break;
+ case RPORT_EV_LOGO:
+ case RPORT_EV_FAILED:
+ case RPORT_EV_STOP:
+ if (rport->port_id == FC_FID_DIR_SERV) {
+ mutex_lock(&lport->lp_mutex);
+ lport->dns_rp = NULL;
+ mutex_unlock(&lport->lp_mutex);
+
+ } else
+ FC_DEBUG_LPORT("Received an event for port (%6x) "
+ "which is not the directory server\n",
+ rport->port_id);
+ break;
+ case RPORT_EV_NONE:
+ break;
+ }
+}
+
+/**
+ * fc_lport_state - Return a string which represents the lport's state
+ * @lport: The lport whose state is to converted to a string
+ */
+static const char *fc_lport_state(struct fc_lport *lport)
+{
+ const char *cp;
+
+ cp = fc_lport_state_names[lport->state];
+ if (!cp)
+ cp = "unknown";
+ return cp;
+}
+
+/**
+ * fc_lport_ptp_setup - Create an rport for point-to-point mode
+ * @lport: The lport to attach the ptp rport to
+ * @fid: The FID of the ptp rport
+ * @remote_wwpn: The WWPN of the ptp rport
+ * @remote_wwnn: The WWNN of the ptp rport
+ */
+static void fc_lport_ptp_setup(struct fc_lport *lport,
+ u32 remote_fid, u64 remote_wwpn,
+ u64 remote_wwnn)
+{
+ struct fc_disc_port dp;
+
+ dp.lp = lport;
+ dp.ids.port_id = remote_fid;
+ dp.ids.port_name = remote_wwpn;
+ dp.ids.node_name = remote_wwnn;
+ dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
+ if (lport->ptp_rp) {
+ lport->tt.rport_logoff(lport->ptp_rp);
+ lport->ptp_rp = NULL;
+ }
+
+ lport->ptp_rp = fc_rport_rogue_create(&dp);
+
+ lport->tt.rport_login(lport->ptp_rp);
+
+ fc_lport_enter_ready(lport);
+}
+
+void fc_get_host_port_type(struct Scsi_Host *shost)
+{
+ /* TODO - currently just NPORT */
+ fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
+}
+EXPORT_SYMBOL(fc_get_host_port_type);
+
+void fc_get_host_port_state(struct Scsi_Host *shost)
+{
+ struct fc_lport *lp = shost_priv(shost);
+
+ if ((lp->link_status & FC_LINK_UP) == FC_LINK_UP)
+ fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ else
+ fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+}
+EXPORT_SYMBOL(fc_get_host_port_state);
+
+void fc_get_host_speed(struct Scsi_Host *shost)
+{
+ struct fc_lport *lport = shost_priv(shost);
+
+ fc_host_speed(shost) = lport->link_speed;
+}
+EXPORT_SYMBOL(fc_get_host_speed);
+
+struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
+{
+ int i;
+ struct fc_host_statistics *fcoe_stats;
+ struct fc_lport *lp = shost_priv(shost);
+ struct timespec v0, v1;
+
+ fcoe_stats = &lp->host_stats;
+ memset(fcoe_stats, 0, sizeof(struct fc_host_statistics));
+
+ jiffies_to_timespec(jiffies, &v0);
+ jiffies_to_timespec(lp->boot_time, &v1);
+ fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec);
+
+ for_each_online_cpu(i) {
+ struct fcoe_dev_stats *stats = lp->dev_stats[i];
+ if (stats == NULL)
+ continue;
+ fcoe_stats->tx_frames += stats->TxFrames;
+ fcoe_stats->tx_words += stats->TxWords;
+ fcoe_stats->rx_frames += stats->RxFrames;
+ fcoe_stats->rx_words += stats->RxWords;
+ fcoe_stats->error_frames += stats->ErrorFrames;
+ fcoe_stats->invalid_crc_count += stats->InvalidCRCCount;
+ fcoe_stats->fcp_input_requests += stats->InputRequests;
+ fcoe_stats->fcp_output_requests += stats->OutputRequests;
+ fcoe_stats->fcp_control_requests += stats->ControlRequests;
+ fcoe_stats->fcp_input_megabytes += stats->InputMegabytes;
+ fcoe_stats->fcp_output_megabytes += stats->OutputMegabytes;
+ fcoe_stats->link_failure_count += stats->LinkFailureCount;
+ }
+ fcoe_stats->lip_count = -1;
+ fcoe_stats->nos_count = -1;
+ fcoe_stats->loss_of_sync_count = -1;
+ fcoe_stats->loss_of_signal_count = -1;
+ fcoe_stats->prim_seq_protocol_err_count = -1;
+ fcoe_stats->dumped_frames = -1;
+ return fcoe_stats;
+}
+EXPORT_SYMBOL(fc_get_host_stats);
+
+/*
+ * Fill in FLOGI command for request.
+ */
+static void
+fc_lport_flogi_fill(struct fc_lport *lport, struct fc_els_flogi *flogi,
+ unsigned int op)
+{
+ struct fc_els_csp *sp;
+ struct fc_els_cssp *cp;
+
+ memset(flogi, 0, sizeof(*flogi));
+ flogi->fl_cmd = (u8) op;
+ put_unaligned_be64(lport->wwpn, &flogi->fl_wwpn);
+ put_unaligned_be64(lport->wwnn, &flogi->fl_wwnn);
+ sp = &flogi->fl_csp;
+ sp->sp_hi_ver = 0x20;
+ sp->sp_lo_ver = 0x20;
+ sp->sp_bb_cred = htons(10); /* this gets set by gateway */
+ sp->sp_bb_data = htons((u16) lport->mfs);
+ cp = &flogi->fl_cssp[3 - 1]; /* class 3 parameters */
+ cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ);
+ if (op != ELS_FLOGI) {
+ sp->sp_features = htons(FC_SP_FT_CIRO);
+ sp->sp_tot_seq = htons(255); /* seq. we accept */
+ sp->sp_rel_off = htons(0x1f);
+ sp->sp_e_d_tov = htonl(lport->e_d_tov);
+
+ cp->cp_rdfs = htons((u16) lport->mfs);
+ cp->cp_con_seq = htons(255);
+ cp->cp_open_seq = 1;
+ }
+}
+
+/*
+ * Add a supported FC-4 type.
+ */
+static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
+{
+ __be32 *mp;
+
+ mp = &lport->fcts.ff_type_map[type / FC_NS_BPW];
+ *mp = htonl(ntohl(*mp) | 1UL << (type % FC_NS_BPW));
+}
+
+/**
+ * fc_lport_recv_rlir_req - Handle received Registered Link Incident Report.
+ * @lport: Fibre Channel local port recieving the RLIR
+ * @sp: current sequence in the RLIR exchange
+ * @fp: RLIR request frame
+ *
+ * Locking Note: The lport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_lport *lport)
+{
+ FC_DEBUG_LPORT("Received RLIR request while in state %s\n",
+ fc_lport_state(lport));
+
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_lport_recv_echo_req - Handle received ECHO request
+ * @lport: Fibre Channel local port recieving the ECHO
+ * @sp: current sequence in the ECHO exchange
+ * @fp: ECHO request frame
+ *
+ * Locking Note: The lport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
+ struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ unsigned int len;
+ void *pp;
+ void *dp;
+ u32 f_ctl;
+
+ FC_DEBUG_LPORT("Received RLIR request while in state %s\n",
+ fc_lport_state(lport));
+
+ len = fr_len(in_fp) - sizeof(struct fc_frame_header);
+ pp = fc_frame_payload_get(in_fp, len);
+
+ if (len < sizeof(__be32))
+ len = sizeof(__be32);
+
+ fp = fc_frame_alloc(lport, len);
+ if (fp) {
+ dp = fc_frame_payload_get(fp, len);
+ memcpy(dp, pp, len);
+ *((u32 *)dp) = htonl(ELS_LS_ACC << 24);
+ sp = lport->tt.seq_start_next(sp);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+ }
+ fc_frame_free(in_fp);
+}
+
+/**
+ * fc_lport_recv_echo_req - Handle received Request Node ID data request
+ * @lport: Fibre Channel local port recieving the RNID
+ * @sp: current sequence in the RNID exchange
+ * @fp: RNID request frame
+ *
+ * Locking Note: The lport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
+ struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ struct fc_els_rnid *req;
+ struct {
+ struct fc_els_rnid_resp rnid;
+ struct fc_els_rnid_cid cid;
+ struct fc_els_rnid_gen gen;
+ } *rp;
+ struct fc_seq_els_data rjt_data;
+ u8 fmt;
+ size_t len;
+ u32 f_ctl;
+
+ FC_DEBUG_LPORT("Received RNID request while in state %s\n",
+ fc_lport_state(lport));
+
+ req = fc_frame_payload_get(in_fp, sizeof(*req));
+ if (!req) {
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_LOGIC;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ } else {
+ fmt = req->rnid_fmt;
+ len = sizeof(*rp);
+ if (fmt != ELS_RNIDF_GEN ||
+ ntohl(lport->rnid_gen.rnid_atype) == 0) {
+ fmt = ELS_RNIDF_NONE; /* nothing to provide */
+ len -= sizeof(rp->gen);
+ }
+ fp = fc_frame_alloc(lport, len);
+ if (fp) {
+ rp = fc_frame_payload_get(fp, len);
+ memset(rp, 0, len);
+ rp->rnid.rnid_cmd = ELS_LS_ACC;
+ rp->rnid.rnid_fmt = fmt;
+ rp->rnid.rnid_cid_len = sizeof(rp->cid);
+ rp->cid.rnid_wwpn = htonll(lport->wwpn);
+ rp->cid.rnid_wwnn = htonll(lport->wwnn);
+ if (fmt == ELS_RNIDF_GEN) {
+ rp->rnid.rnid_sid_len = sizeof(rp->gen);
+ memcpy(&rp->gen, &lport->rnid_gen,
+ sizeof(rp->gen));
+ }
+ sp = lport->tt.seq_start_next(sp);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+ f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+ }
+ }
+ fc_frame_free(in_fp);
+}
+
+/**
+ * fc_lport_recv_adisc_req - Handle received Address Discovery Request
+ * @lport: Fibre Channel local port recieving the ADISC
+ * @sp: current sequence in the ADISC exchange
+ * @fp: ADISC request frame
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_adisc_req(struct fc_seq *sp, struct fc_frame *in_fp,
+ struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ struct fc_els_adisc *req, *rp;
+ struct fc_seq_els_data rjt_data;
+ size_t len;
+ u32 f_ctl;
+
+ FC_DEBUG_LPORT("Received ADISC request while in state %s\n",
+ fc_lport_state(lport));
+
+ req = fc_frame_payload_get(in_fp, sizeof(*req));
+ if (!req) {
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_LOGIC;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ } else {
+ len = sizeof(*rp);
+ fp = fc_frame_alloc(lport, len);
+ if (fp) {
+ rp = fc_frame_payload_get(fp, len);
+ memset(rp, 0, len);
+ rp->adisc_cmd = ELS_LS_ACC;
+ rp->adisc_wwpn = htonll(lport->wwpn);
+ rp->adisc_wwnn = htonll(lport->wwnn);
+ hton24(rp->adisc_port_id,
+ fc_host_port_id(lport->host));
+ sp = lport->tt.seq_start_next(sp);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+ f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+ }
+ }
+ fc_frame_free(in_fp);
+}
+
+/**
+ * fc_lport_recv_logo_req - Handle received fabric LOGO request
+ * @lport: Fibre Channel local port recieving the LOGO
+ * @sp: current sequence in the LOGO exchange
+ * @fp: LOGO request frame
+ *
+ * Locking Note: The lport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_logo_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_lport *lport)
+{
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+ fc_lport_enter_reset(lport);
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_fabric_login - Start the lport state machine
+ * @lport: The lport that should log into the fabric
+ *
+ * Locking Note: This function should not be called
+ * with the lport lock held.
+ */
+int fc_fabric_login(struct fc_lport *lport)
+{
+ int rc = -1;
+
+ mutex_lock(&lport->lp_mutex);
+ if (lport->state == LPORT_ST_NONE) {
+ fc_lport_enter_reset(lport);
+ rc = 0;
+ }
+ mutex_unlock(&lport->lp_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(fc_fabric_login);
+
+/**
+ * fc_linkup - Handler for transport linkup events
+ * @lport: The lport whose link is up
+ */
+void fc_linkup(struct fc_lport *lport)
+{
+ FC_DEBUG_LPORT("Link is up for port (%6x)\n",
+ fc_host_port_id(lport->host));
+
+ mutex_lock(&lport->lp_mutex);
+ if ((lport->link_status & FC_LINK_UP) != FC_LINK_UP) {
+ lport->link_status |= FC_LINK_UP;
+
+ if (lport->state == LPORT_ST_RESET)
+ fc_lport_enter_flogi(lport);
+ }
+ mutex_unlock(&lport->lp_mutex);
+}
+EXPORT_SYMBOL(fc_linkup);
+
+/**
+ * fc_linkdown - Handler for transport linkdown events
+ * @lport: The lport whose link is down
+ */
+void fc_linkdown(struct fc_lport *lport)
+{
+ mutex_lock(&lport->lp_mutex);
+ FC_DEBUG_LPORT("Link is down for port (%6x)\n",
+ fc_host_port_id(lport->host));
+
+ if ((lport->link_status & FC_LINK_UP) == FC_LINK_UP) {
+ lport->link_status &= ~(FC_LINK_UP);
+ fc_lport_enter_reset(lport);
+ lport->tt.fcp_cleanup(lport);
+ }
+ mutex_unlock(&lport->lp_mutex);
+}
+EXPORT_SYMBOL(fc_linkdown);
+
+/**
+ * fc_pause - Pause the flow of frames
+ * @lport: The lport to be paused
+ */
+void fc_pause(struct fc_lport *lport)
+{
+ mutex_lock(&lport->lp_mutex);
+ lport->link_status |= FC_PAUSE;
+ mutex_unlock(&lport->lp_mutex);
+}
+EXPORT_SYMBOL(fc_pause);
+
+/**
+ * fc_unpause - Unpause the flow of frames
+ * @lport: The lport to be unpaused
+ */
+void fc_unpause(struct fc_lport *lport)
+{
+ mutex_lock(&lport->lp_mutex);
+ lport->link_status &= ~(FC_PAUSE);
+ mutex_unlock(&lport->lp_mutex);
+}
+EXPORT_SYMBOL(fc_unpause);
+
+/**
+ * fc_fabric_logoff - Logout of the fabric
+ * @lport: fc_lport pointer to logoff the fabric
+ *
+ * Return value:
+ * 0 for success, -1 for failure
+ **/
+int fc_fabric_logoff(struct fc_lport *lport)
+{
+ lport->tt.disc_stop_final(lport);
+ mutex_lock(&lport->lp_mutex);
+ fc_lport_enter_logo(lport);
+ mutex_unlock(&lport->lp_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(fc_fabric_logoff);
+
+/**
+ * fc_lport_destroy - unregister a fc_lport
+ * @lport: fc_lport pointer to unregister
+ *
+ * Return value:
+ * None
+ * Note:
+ * exit routine for fc_lport instance
+ * clean-up all the allocated memory
+ * and free up other system resources.
+ *
+ **/
+int fc_lport_destroy(struct fc_lport *lport)
+{
+ lport->tt.frame_send = fc_frame_drop;
+ lport->tt.fcp_abort_io(lport);
+ lport->tt.exch_mgr_reset(lport->emp, 0, 0);
+ return 0;
+}
+EXPORT_SYMBOL(fc_lport_destroy);
+
+/**
+ * fc_set_mfs - sets up the mfs for the corresponding fc_lport
+ * @lport: fc_lport pointer to unregister
+ * @mfs: the new mfs for fc_lport
+ *
+ * Set mfs for the given fc_lport to the new mfs.
+ *
+ * Return: 0 for success
+ *
+ **/
+int fc_set_mfs(struct fc_lport *lport, u32 mfs)
+{
+ unsigned int old_mfs;
+ int rc = -EINVAL;
+
+ mutex_lock(&lport->lp_mutex);
+
+ old_mfs = lport->mfs;
+
+ if (mfs >= FC_MIN_MAX_FRAME) {
+ mfs &= ~3;
+ if (mfs > FC_MAX_FRAME)
+ mfs = FC_MAX_FRAME;
+ mfs -= sizeof(struct fc_frame_header);
+ lport->mfs = mfs;
+ rc = 0;
+ }
+
+ if (!rc && mfs < old_mfs)
+ fc_lport_enter_reset(lport);
+
+ mutex_unlock(&lport->lp_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(fc_set_mfs);
+
+/**
+ * fc_lport_disc_callback - Callback for discovery events
+ * @lport: FC local port
+ * @event: The discovery event
+ */
+void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
+{
+ switch (event) {
+ case DISC_EV_SUCCESS:
+ FC_DEBUG_LPORT("Got a SUCCESS event for port (%6x)\n",
+ fc_host_port_id(lport->host));
+ break;
+ case DISC_EV_FAILED:
+ FC_DEBUG_LPORT("Got a FAILED event for port (%6x)\n",
+ fc_host_port_id(lport->host));
+ mutex_lock(&lport->lp_mutex);
+ fc_lport_enter_reset(lport);
+ mutex_unlock(&lport->lp_mutex);
+ break;
+ case DISC_EV_NONE:
+ WARN_ON(1);
+ break;
+ }
+}
+
+/**
+ * fc_rport_enter_ready - Enter the ready state and start discovery
+ * @lport: Fibre Channel local port that is ready
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_ready(struct fc_lport *lport)
+{
+ FC_DEBUG_LPORT("Port (%6x) entered Ready from state %s\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_READY);
+
+ lport->tt.disc_start(fc_lport_disc_callback, lport);
+}
+
+/**
+ * fc_lport_recv_flogi_req - Receive a FLOGI request
+ * @sp_in: The sequence the FLOGI is on
+ * @rx_fp: The frame the FLOGI is in
+ * @lport: The lport that recieved the request
+ *
+ * A received FLOGI request indicates a point-to-point connection.
+ * Accept it with the common service parameters indicating our N port.
+ * Set up to do a PLOGI if we have the higher-number WWPN.
+ *
+ * Locking Note: The lport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
+ struct fc_frame *rx_fp,
+ struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+ struct fc_frame_header *fh;
+ struct fc_seq *sp;
+ struct fc_exch *ep;
+ struct fc_els_flogi *flp;
+ struct fc_els_flogi *new_flp;
+ u64 remote_wwpn;
+ u32 remote_fid;
+ u32 local_fid;
+ u32 f_ctl;
+
+ FC_DEBUG_LPORT("Received FLOGI request while in state %s\n",
+ fc_lport_state(lport));
+
+ fh = fc_frame_header_get(rx_fp);
+ remote_fid = ntoh24(fh->fh_s_id);
+ flp = fc_frame_payload_get(rx_fp, sizeof(*flp));
+ if (!flp)
+ goto out;
+ remote_wwpn = get_unaligned_be64(&flp->fl_wwpn);
+ if (remote_wwpn == lport->wwpn) {
+ FC_DBG("FLOGI from port with same WWPN %llx "
+ "possible configuration error\n", remote_wwpn);
+ goto out;
+ }
+ FC_DBG("FLOGI from port WWPN %llx\n", remote_wwpn);
+
+ /*
+ * XXX what is the right thing to do for FIDs?
+ * The originator might expect our S_ID to be 0xfffffe.
+ * But if so, both of us could end up with the same FID.
+ */
+ local_fid = FC_LOCAL_PTP_FID_LO;
+ if (remote_wwpn < lport->wwpn) {
+ local_fid = FC_LOCAL_PTP_FID_HI;
+ if (!remote_fid || remote_fid == local_fid)
+ remote_fid = FC_LOCAL_PTP_FID_LO;
+ } else if (!remote_fid) {
+ remote_fid = FC_LOCAL_PTP_FID_HI;
+ }
+
+ fc_host_port_id(lport->host) = local_fid;
+
+ fp = fc_frame_alloc(lport, sizeof(*flp));
+ if (fp) {
+ sp = lport->tt.seq_start_next(fr_seq(rx_fp));
+ new_flp = fc_frame_payload_get(fp, sizeof(*flp));
+ fc_lport_flogi_fill(lport, new_flp, ELS_FLOGI);
+ new_flp->fl_cmd = (u8) ELS_LS_ACC;
+
+ /*
+ * Send the response. If this fails, the originator should
+ * repeat the sequence.
+ */
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+ ep = fc_seq_exch(sp);
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+
+ } else {
+ fc_lport_error(lport, fp);
+ }
+ fc_lport_ptp_setup(lport, remote_fid, remote_wwpn,
+ get_unaligned_be64(&flp->fl_wwnn));
+
+ lport->tt.disc_start(fc_lport_disc_callback, lport);
+
+out:
+ sp = fr_seq(rx_fp);
+ fc_frame_free(rx_fp);
+}
+
+/**
+ * fc_lport_recv_req - The generic lport request handler
+ * @lport: The lport that received the request
+ * @sp: The sequence the request is on
+ * @fp: The frame the request is in
+ *
+ * This function will see if the lport handles the request or
+ * if an rport should handle the request.
+ *
+ * Locking Note: This function should not be called with the lport
+ * lock held becuase it will grab the lock.
+ */
+static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ void (*recv) (struct fc_seq *, struct fc_frame *, struct fc_lport *);
+ struct fc_rport *rport;
+ u32 s_id;
+ u32 d_id;
+ struct fc_seq_els_data rjt_data;
+
+ mutex_lock(&lport->lp_mutex);
+
+ /*
+ * Handle special ELS cases like FLOGI, LOGO, and
+ * RSCN here. These don't require a session.
+ * Even if we had a session, it might not be ready.
+ */
+ if (fh->fh_type == FC_TYPE_ELS && fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
+ /*
+ * Check opcode.
+ */
+ recv = NULL;
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_FLOGI:
+ recv = fc_lport_recv_flogi_req;
+ break;
+ case ELS_LOGO:
+ fh = fc_frame_header_get(fp);
+ if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI)
+ recv = fc_lport_recv_logo_req;
+ break;
+ case ELS_RSCN:
+ recv = lport->tt.disc_recv_req;
+ break;
+ case ELS_ECHO:
+ recv = fc_lport_recv_echo_req;
+ break;
+ case ELS_RLIR:
+ recv = fc_lport_recv_rlir_req;
+ break;
+ case ELS_RNID:
+ recv = fc_lport_recv_rnid_req;
+ break;
+ case ELS_ADISC:
+ recv = fc_lport_recv_adisc_req;
+ break;
+ }
+
+ if (recv)
+ recv(sp, fp, lport);
+ else {
+ /*
+ * Find session.
+ * If this is a new incoming PLOGI, we won't find it.
+ */
+ s_id = ntoh24(fh->fh_s_id);
+ d_id = ntoh24(fh->fh_d_id);
+
+ rport = lport->tt.rport_lookup(lport, s_id);
+ if (rport)
+ lport->tt.rport_recv_req(sp, fp, rport);
+ else {
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp,
+ ELS_LS_RJT,
+ &rjt_data);
+ fc_frame_free(fp);
+ }
+ }
+ } else {
+ FC_DBG("dropping invalid frame (eof %x)\n", fr_eof(fp));
+ fc_frame_free(fp);
+ }
+ mutex_unlock(&lport->lp_mutex);
+
+ /*
+ * The common exch_done for all request may not be good
+ * if any request requires longer hold on exhange. XXX
+ */
+ lport->tt.exch_done(sp);
+}
+
+/**
+ * fc_lport_reset - Reset an lport
+ * @lport: The lport which should be reset
+ *
+ * Locking Note: This functions should not be called with the
+ * lport lock held.
+ */
+int fc_lport_reset(struct fc_lport *lport)
+{
+ mutex_lock(&lport->lp_mutex);
+ fc_lport_enter_reset(lport);
+ mutex_unlock(&lport->lp_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(fc_lport_reset);
+
+/**
+ * fc_rport_enter_reset - Reset the local port
+ * @lport: Fibre Channel local port to be reset
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_reset(struct fc_lport *lport)
+{
+ FC_DEBUG_LPORT("Port (%6x) entered RESET state from %s state\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_RESET);
+
+ if (lport->dns_rp)
+ lport->tt.rport_logoff(lport->dns_rp);
+
+ if (lport->ptp_rp) {
+ lport->tt.rport_logoff(lport->ptp_rp);
+ lport->ptp_rp = NULL;
+ }
+
+ lport->tt.disc_stop(lport);
+
+ lport->tt.exch_mgr_reset(lport->emp, 0, 0);
+ fc_host_fabric_name(lport->host) = 0;
+ fc_host_port_id(lport->host) = 0;
+
+ if ((lport->link_status & FC_LINK_UP) == FC_LINK_UP)
+ fc_lport_enter_flogi(lport);
+}
+
+/**
+ * fc_lport_error - Handler for any errors
+ * @lport: The fc_lport object
+ * @fp: The frame pointer
+ *
+ * If the error was caused by a resource allocation failure
+ * then wait for half a second and retry, otherwise retry
+ * after the e_d_tov time.
+ */
+static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
+{
+ unsigned long delay = 0;
+ FC_DEBUG_LPORT("Error %ld in state %s, retries %d\n",
+ PTR_ERR(fp), fc_lport_state(lport),
+ lport->retry_count);
+
+ if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
+ /*
+ * Memory allocation failure, or the exchange timed out.
+ * Retry after delay
+ */
+ if (lport->retry_count < lport->max_retry_count) {
+ lport->retry_count++;
+ if (!fp)
+ delay = msecs_to_jiffies(500);
+ else
+ delay = msecs_to_jiffies(lport->e_d_tov);
+
+ schedule_delayed_work(&lport->retry_work, delay);
+ } else {
+ switch (lport->state) {
+ case LPORT_ST_NONE:
+ case LPORT_ST_READY:
+ case LPORT_ST_RESET:
+ case LPORT_ST_RPN_ID:
+ case LPORT_ST_RFT_ID:
+ case LPORT_ST_SCR:
+ case LPORT_ST_DNS:
+ case LPORT_ST_FLOGI:
+ case LPORT_ST_LOGO:
+ fc_lport_enter_reset(lport);
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * fc_lport_rft_id_resp - Handle response to Register Fibre
+ * Channel Types by ID (RPN_ID) request
+ * @sp: current sequence in RPN_ID exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel host port instance
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error
+ * and then unlock the lport.
+ */
+static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
+{
+ struct fc_lport *lport = lp_arg;
+ struct fc_frame_header *fh;
+ struct fc_ct_hdr *ct;
+
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ return;
+
+ mutex_lock(&lport->lp_mutex);
+
+ FC_DEBUG_LPORT("Received a RFT_ID response\n");
+
+ if (lport->state != LPORT_ST_RFT_ID) {
+ FC_DBG("Received a RFT_ID response, but in state %s\n",
+ fc_lport_state(lport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
+ fh = fc_frame_header_get(fp);
+ ct = fc_frame_payload_get(fp, sizeof(*ct));
+
+ if (fh && ct && fh->fh_type == FC_TYPE_CT &&
+ ct->ct_fs_type == FC_FST_DIR &&
+ ct->ct_fs_subtype == FC_NS_SUBTYPE &&
+ ntohs(ct->ct_cmd) == FC_FS_ACC)
+ fc_lport_enter_scr(lport);
+ else
+ fc_lport_error(lport, fp);
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_lport_rpn_id_resp - Handle response to Register Port
+ * Name by ID (RPN_ID) request
+ * @sp: current sequence in RPN_ID exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel host port instance
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error
+ * and then unlock the lport.
+ */
+static void fc_lport_rpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
+{
+ struct fc_lport *lport = lp_arg;
+ struct fc_frame_header *fh;
+ struct fc_ct_hdr *ct;
+
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ return;
+
+ mutex_lock(&lport->lp_mutex);
+
+ FC_DEBUG_LPORT("Received a RPN_ID response\n");
+
+ if (lport->state != LPORT_ST_RPN_ID) {
+ FC_DBG("Received a RPN_ID response, but in state %s\n",
+ fc_lport_state(lport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
+ fh = fc_frame_header_get(fp);
+ ct = fc_frame_payload_get(fp, sizeof(*ct));
+ if (fh && ct && fh->fh_type == FC_TYPE_CT &&
+ ct->ct_fs_type == FC_FST_DIR &&
+ ct->ct_fs_subtype == FC_NS_SUBTYPE &&
+ ntohs(ct->ct_cmd) == FC_FS_ACC)
+ fc_lport_enter_rft_id(lport);
+ else
+ fc_lport_error(lport, fp);
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_lport_scr_resp - Handle response to State Change Register (SCR) request
+ * @sp: current sequence in SCR exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel lport port instance that sent the registration request
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error
+ * and then unlock the lport.
+ */
+static void fc_lport_scr_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
+{
+ struct fc_lport *lport = lp_arg;
+ u8 op;
+
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ return;
+
+ mutex_lock(&lport->lp_mutex);
+
+ FC_DEBUG_LPORT("Received a SCR response\n");
+
+ if (lport->state != LPORT_ST_SCR) {
+ FC_DBG("Received a SCR response, but in state %s\n",
+ fc_lport_state(lport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC)
+ fc_lport_enter_ready(lport);
+ else
+ fc_lport_error(lport, fp);
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_lport_enter_scr - Send a State Change Register (SCR) request
+ * @lport: Fibre Channel local port to register for state changes
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_scr(struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+
+ FC_DEBUG_LPORT("Port (%6x) entered SCR state from %s state\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_SCR);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_scr));
+ if (!fp) {
+ fc_lport_error(lport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, NULL, fp, ELS_SCR,
+ fc_lport_scr_resp, lport, lport->e_d_tov))
+ fc_lport_error(lport, fp);
+}
+
+/**
+ * fc_lport_enter_rft_id - Register FC4-types with the name server
+ * @lport: Fibre Channel local port to register
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_rft_id(struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+ struct fc_ns_fts *lps;
+ int i;
+
+ FC_DEBUG_LPORT("Port (%6x) entered RFT_ID state from %s state\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_RFT_ID);
+
+ lps = &lport->fcts;
+ i = sizeof(lps->ff_type_map) / sizeof(lps->ff_type_map[0]);
+ while (--i >= 0)
+ if (ntohl(lps->ff_type_map[i]) != 0)
+ break;
+ if (i < 0) {
+ /* nothing to register, move on to SCR */
+ fc_lport_enter_scr(lport);
+ return;
+ }
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+ sizeof(struct fc_ns_rft));
+ if (!fp) {
+ fc_lport_error(lport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, NULL, fp, FC_NS_RFT_ID,
+ fc_lport_rft_id_resp,
+ lport, lport->e_d_tov))
+ fc_lport_error(lport, fp);
+}
+
+/**
+ * fc_rport_enter_rft_id - Register port name with the name server
+ * @lport: Fibre Channel local port to register
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_rpn_id(struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+
+ FC_DEBUG_LPORT("Port (%6x) entered RPN_ID state from %s state\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_RPN_ID);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+ sizeof(struct fc_ns_rn_id));
+ if (!fp) {
+ fc_lport_error(lport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, NULL, fp, FC_NS_RPN_ID,
+ fc_lport_rpn_id_resp,
+ lport, lport->e_d_tov))
+ fc_lport_error(lport, fp);
+}
+
+static struct fc_rport_operations fc_lport_rport_ops = {
+ .event_callback = fc_lport_rport_callback,
+};
+
+/**
+ * fc_rport_enter_dns - Create a rport to the name server
+ * @lport: Fibre Channel local port requesting a rport for the name server
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_dns(struct fc_lport *lport)
+{
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rdata;
+ struct fc_disc_port dp;
+
+ dp.ids.port_id = FC_FID_DIR_SERV;
+ dp.ids.port_name = -1;
+ dp.ids.node_name = -1;
+ dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ dp.lp = lport;
+
+ FC_DEBUG_LPORT("Port (%6x) entered DNS state from %s state\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_DNS);
+
+ rport = fc_rport_rogue_create(&dp);
+ if (!rport)
+ goto err;
+
+ rdata = rport->dd_data;
+ rdata->ops = &fc_lport_rport_ops;
+ lport->tt.rport_login(rport);
+ return;
+
+err:
+ fc_lport_error(lport, NULL);
+}
+
+/**
+ * fc_lport_timeout - Handler for the retry_work timer.
+ * @work: The work struct of the fc_lport
+ */
+static void fc_lport_timeout(struct work_struct *work)
+{
+ struct fc_lport *lport =
+ container_of(work, struct fc_lport,
+ retry_work.work);
+
+ mutex_lock(&lport->lp_mutex);
+
+ switch (lport->state) {
+ case LPORT_ST_NONE:
+ case LPORT_ST_READY:
+ case LPORT_ST_RESET:
+ WARN_ON(1);
+ break;
+ case LPORT_ST_FLOGI:
+ fc_lport_enter_flogi(lport);
+ break;
+ case LPORT_ST_DNS:
+ fc_lport_enter_dns(lport);
+ break;
+ case LPORT_ST_RPN_ID:
+ fc_lport_enter_rpn_id(lport);
+ break;
+ case LPORT_ST_RFT_ID:
+ fc_lport_enter_rft_id(lport);
+ break;
+ case LPORT_ST_SCR:
+ fc_lport_enter_scr(lport);
+ break;
+ case LPORT_ST_LOGO:
+ fc_lport_enter_logo(lport);
+ break;
+ }
+
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_lport_logo_resp - Handle response to LOGO request
+ * @sp: current sequence in LOGO exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel lport port instance that sent the LOGO request
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error
+ * and then unlock the lport.
+ */
+static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
+{
+ struct fc_lport *lport = lp_arg;
+ u8 op;
+
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ return;
+
+ mutex_lock(&lport->lp_mutex);
+
+ FC_DEBUG_LPORT("Received a LOGO response\n");
+
+ if (lport->state != LPORT_ST_LOGO) {
+ FC_DBG("Received a LOGO response, but in state %s\n",
+ fc_lport_state(lport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC)
+ fc_lport_enter_reset(lport);
+ else
+ fc_lport_error(lport, fp);
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_rport_enter_logo - Logout of the fabric
+ * @lport: Fibre Channel local port to be logged out
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_logo(struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+ struct fc_els_logo *logo;
+
+ FC_DEBUG_LPORT("Port (%6x) entered LOGO state from %s state\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_LOGO);
+
+ /* DNS session should be closed so we can release it here */
+ if (lport->dns_rp)
+ lport->tt.rport_logoff(lport->dns_rp);
+
+ fp = fc_frame_alloc(lport, sizeof(*logo));
+ if (!fp) {
+ fc_lport_error(lport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, NULL, fp, ELS_LOGO, fc_lport_logo_resp,
+ lport, lport->e_d_tov))
+ fc_lport_error(lport, fp);
+}
+
+/**
+ * fc_lport_flogi_resp - Handle response to FLOGI request
+ * @sp: current sequence in FLOGI exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel lport port instance that sent the FLOGI request
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error
+ * and then unlock the lport.
+ */
+static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
+{
+ struct fc_lport *lport = lp_arg;
+ struct fc_frame_header *fh;
+ struct fc_els_flogi *flp;
+ u32 did;
+ u16 csp_flags;
+ unsigned int r_a_tov;
+ unsigned int e_d_tov;
+ u16 mfs;
+
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ return;
+
+ mutex_lock(&lport->lp_mutex);
+
+ FC_DEBUG_LPORT("Received a FLOGI response\n");
+
+ if (lport->state != LPORT_ST_FLOGI) {
+ FC_DBG("Received a FLOGI response, but in state %s\n",
+ fc_lport_state(lport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
+ fh = fc_frame_header_get(fp);
+ did = ntoh24(fh->fh_d_id);
+ if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) {
+
+ FC_DEBUG_LPORT("Assigned fid %x\n", did);
+ fc_host_port_id(lport->host) = did;
+
+ flp = fc_frame_payload_get(fp, sizeof(*flp));
+ if (flp) {
+ mfs = ntohs(flp->fl_csp.sp_bb_data) &
+ FC_SP_BB_DATA_MASK;
+ if (mfs >= FC_SP_MIN_MAX_PAYLOAD &&
+ mfs < lport->mfs)
+ lport->mfs = mfs;
+ csp_flags = ntohs(flp->fl_csp.sp_features);
+ r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov);
+ e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov);
+ if (csp_flags & FC_SP_FT_EDTR)
+ e_d_tov /= 1000000;
+ if ((csp_flags & FC_SP_FT_FPORT) == 0) {
+ if (e_d_tov > lport->e_d_tov)
+ lport->e_d_tov = e_d_tov;
+ lport->r_a_tov = 2 * e_d_tov;
+ FC_DBG("Point-to-Point mode\n");
+ fc_lport_ptp_setup(lport, ntoh24(fh->fh_s_id),
+ get_unaligned_be64(
+ &flp->fl_wwpn),
+ get_unaligned_be64(
+ &flp->fl_wwnn));
+ } else {
+ lport->e_d_tov = e_d_tov;
+ lport->r_a_tov = r_a_tov;
+ fc_host_fabric_name(lport->host) =
+ get_unaligned_be64(&flp->fl_wwnn);
+ fc_lport_enter_dns(lport);
+ }
+ }
+
+ if (flp) {
+ csp_flags = ntohs(flp->fl_csp.sp_features);
+ if ((csp_flags & FC_SP_FT_FPORT) == 0) {
+ lport->tt.disc_start(fc_lport_disc_callback,
+ lport);
+ }
+ }
+ } else {
+ FC_DBG("bad FLOGI response\n");
+ }
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_rport_enter_flogi - Send a FLOGI request to the fabric manager
+ * @lport: Fibre Channel local port to be logged in to the fabric
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+void fc_lport_enter_flogi(struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+
+ FC_DEBUG_LPORT("Processing FLOGI state\n");
+
+ fc_lport_state_enter(lport, LPORT_ST_FLOGI);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
+ if (!fp)
+ return fc_lport_error(lport, fp);
+
+ if (!lport->tt.elsct_send(lport, NULL, fp, ELS_FLOGI,
+ fc_lport_flogi_resp, lport, lport->e_d_tov))
+ fc_lport_error(lport, fp);
+}
+
+/* Configure a fc_lport */
+int fc_lport_config(struct fc_lport *lport)
+{
+ INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout);
+ mutex_init(&lport->lp_mutex);
+
+ fc_lport_state_enter(lport, LPORT_ST_NONE);
+
+ fc_lport_add_fc4_type(lport, FC_TYPE_FCP);
+ fc_lport_add_fc4_type(lport, FC_TYPE_CT);
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_lport_config);
+
+int fc_lport_init(struct fc_lport *lport)
+{
+ if (!lport->tt.lport_recv)
+ lport->tt.lport_recv = fc_lport_recv_req;
+
+ if (!lport->tt.lport_reset)
+ lport->tt.lport_reset = fc_lport_reset;
+
+ fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
+ fc_host_node_name(lport->host) = lport->wwnn;
+ fc_host_port_name(lport->host) = lport->wwpn;
+ fc_host_supported_classes(lport->host) = FC_COS_CLASS3;
+ memset(fc_host_supported_fc4s(lport->host), 0,
+ sizeof(fc_host_supported_fc4s(lport->host)));
+ fc_host_supported_fc4s(lport->host)[2] = 1;
+ fc_host_supported_fc4s(lport->host)[7] = 1;
+
+ /* This value is also unchanging */
+ memset(fc_host_active_fc4s(lport->host), 0,
+ sizeof(fc_host_active_fc4s(lport->host)));
+ fc_host_active_fc4s(lport->host)[2] = 1;
+ fc_host_active_fc4s(lport->host)[7] = 1;
+ fc_host_maxframe_size(lport->host) = lport->mfs;
+ fc_host_supported_speeds(lport->host) = 0;
+ if (lport->link_supported_speeds & FC_PORTSPEED_1GBIT)
+ fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT;
+ if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
+ fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_lport_init);
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
new file mode 100644
index 000000000000..e780d8caf70e
--- /dev/null
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -0,0 +1,1291 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * RPORT GENERAL INFO
+ *
+ * This file contains all processing regarding fc_rports. It contains the
+ * rport state machine and does all rport interaction with the transport class.
+ * There should be no other places in libfc that interact directly with the
+ * transport class in regards to adding and deleting rports.
+ *
+ * fc_rport's represent N_Port's within the fabric.
+ */
+
+/*
+ * RPORT LOCKING
+ *
+ * The rport should never hold the rport mutex and then attempt to acquire
+ * either the lport or disc mutexes. The rport's mutex is considered lesser
+ * than both the lport's mutex and the disc mutex. Refer to fc_lport.c for
+ * more comments on the heirarchy.
+ *
+ * The locking strategy is similar to the lport's strategy. The lock protects
+ * the rport's states and is held and released by the entry points to the rport
+ * block. All _enter_* functions correspond to rport states and expect the rport
+ * mutex to be locked before calling them. This means that rports only handle
+ * one request or response at a time, since they're not critical for the I/O
+ * path this potential over-use of the mutex is acceptable.
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/rcupdate.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <asm/unaligned.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+static int fc_rport_debug;
+
+#define FC_DEBUG_RPORT(fmt...) \
+ do { \
+ if (fc_rport_debug) \
+ FC_DBG(fmt); \
+ } while (0)
+
+struct workqueue_struct *rport_event_queue;
+
+static void fc_rport_enter_plogi(struct fc_rport *);
+static void fc_rport_enter_prli(struct fc_rport *);
+static void fc_rport_enter_rtv(struct fc_rport *);
+static void fc_rport_enter_ready(struct fc_rport *);
+static void fc_rport_enter_logo(struct fc_rport *);
+
+static void fc_rport_recv_plogi_req(struct fc_rport *,
+ struct fc_seq *, struct fc_frame *);
+static void fc_rport_recv_prli_req(struct fc_rport *,
+ struct fc_seq *, struct fc_frame *);
+static void fc_rport_recv_prlo_req(struct fc_rport *,
+ struct fc_seq *, struct fc_frame *);
+static void fc_rport_recv_logo_req(struct fc_rport *,
+ struct fc_seq *, struct fc_frame *);
+static void fc_rport_timeout(struct work_struct *);
+static void fc_rport_error(struct fc_rport *, struct fc_frame *);
+static void fc_rport_work(struct work_struct *);
+
+static const char *fc_rport_state_names[] = {
+ [RPORT_ST_NONE] = "None",
+ [RPORT_ST_INIT] = "Init",
+ [RPORT_ST_PLOGI] = "PLOGI",
+ [RPORT_ST_PRLI] = "PRLI",
+ [RPORT_ST_RTV] = "RTV",
+ [RPORT_ST_READY] = "Ready",
+ [RPORT_ST_LOGO] = "LOGO",
+};
+
+static void fc_rport_rogue_destroy(struct device *dev)
+{
+ struct fc_rport *rport = dev_to_rport(dev);
+ FC_DEBUG_RPORT("Destroying rogue rport (%6x)\n", rport->port_id);
+ kfree(rport);
+}
+
+struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *dp)
+{
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rdata;
+ rport = kzalloc(sizeof(*rport) + sizeof(*rdata), GFP_KERNEL);
+
+ if (!rport)
+ return NULL;
+
+ rdata = RPORT_TO_PRIV(rport);
+
+ rport->dd_data = rdata;
+ rport->port_id = dp->ids.port_id;
+ rport->port_name = dp->ids.port_name;
+ rport->node_name = dp->ids.node_name;
+ rport->roles = dp->ids.roles;
+ rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
+ /*
+ * Note: all this libfc rogue rport code will be removed for
+ * upstream so it fine that this is really ugly and hacky right now.
+ */
+ device_initialize(&rport->dev);
+ rport->dev.release = fc_rport_rogue_destroy;
+
+ mutex_init(&rdata->rp_mutex);
+ rdata->local_port = dp->lp;
+ rdata->trans_state = FC_PORTSTATE_ROGUE;
+ rdata->rp_state = RPORT_ST_INIT;
+ rdata->event = RPORT_EV_NONE;
+ rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
+ rdata->ops = NULL;
+ rdata->e_d_tov = dp->lp->e_d_tov;
+ rdata->r_a_tov = dp->lp->r_a_tov;
+ INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout);
+ INIT_WORK(&rdata->event_work, fc_rport_work);
+ /*
+ * For good measure, but not necessary as we should only
+ * add REAL rport to the lport list.
+ */
+ INIT_LIST_HEAD(&rdata->peers);
+
+ return rport;
+}
+
+/**
+ * fc_rport_state - return a string for the state the rport is in
+ * @rport: The rport whose state we want to get a string for
+ */
+static const char *fc_rport_state(struct fc_rport *rport)
+{
+ const char *cp;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+
+ cp = fc_rport_state_names[rdata->rp_state];
+ if (!cp)
+ cp = "Unknown";
+ return cp;
+}
+
+/**
+ * fc_set_rport_loss_tmo - Set the remote port loss timeout in seconds.
+ * @rport: Pointer to Fibre Channel remote port structure
+ * @timeout: timeout in seconds
+ */
+void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout)
+{
+ if (timeout)
+ rport->dev_loss_tmo = timeout + 5;
+ else
+ rport->dev_loss_tmo = 30;
+}
+EXPORT_SYMBOL(fc_set_rport_loss_tmo);
+
+/**
+ * fc_plogi_get_maxframe - Get max payload from the common service parameters
+ * @flp: FLOGI payload structure
+ * @maxval: upper limit, may be less than what is in the service parameters
+ */
+static unsigned int
+fc_plogi_get_maxframe(struct fc_els_flogi *flp, unsigned int maxval)
+{
+ unsigned int mfs;
+
+ /*
+ * Get max payload from the common service parameters and the
+ * class 3 receive data field size.
+ */
+ mfs = ntohs(flp->fl_csp.sp_bb_data) & FC_SP_BB_DATA_MASK;
+ if (mfs >= FC_SP_MIN_MAX_PAYLOAD && mfs < maxval)
+ maxval = mfs;
+ mfs = ntohs(flp->fl_cssp[3 - 1].cp_rdfs);
+ if (mfs >= FC_SP_MIN_MAX_PAYLOAD && mfs < maxval)
+ maxval = mfs;
+ return maxval;
+}
+
+/**
+ * fc_rport_state_enter - Change the rport's state
+ * @rport: The rport whose state should change
+ * @new: The new state of the rport
+ *
+ * Locking Note: Called with the rport lock held
+ */
+static void fc_rport_state_enter(struct fc_rport *rport,
+ enum fc_rport_state new)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ if (rdata->rp_state != new)
+ rdata->retries = 0;
+ rdata->rp_state = new;
+}
+
+static void fc_rport_work(struct work_struct *work)
+{
+ struct fc_rport_libfc_priv *rdata =
+ container_of(work, struct fc_rport_libfc_priv, event_work);
+ enum fc_rport_event event;
+ enum fc_rport_trans_state trans_state;
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_rport_operations *rport_ops;
+ struct fc_rport *rport = PRIV_TO_RPORT(rdata);
+
+ mutex_lock(&rdata->rp_mutex);
+ event = rdata->event;
+ rport_ops = rdata->ops;
+
+ if (event == RPORT_EV_CREATED) {
+ struct fc_rport *new_rport;
+ struct fc_rport_libfc_priv *new_rdata;
+ struct fc_rport_identifiers ids;
+
+ ids.port_id = rport->port_id;
+ ids.roles = rport->roles;
+ ids.port_name = rport->port_name;
+ ids.node_name = rport->node_name;
+
+ mutex_unlock(&rdata->rp_mutex);
+
+ new_rport = fc_remote_port_add(lport->host, 0, &ids);
+ if (new_rport) {
+ /*
+ * Switch from the rogue rport to the rport
+ * returned by the FC class.
+ */
+ new_rport->maxframe_size = rport->maxframe_size;
+
+ new_rdata = new_rport->dd_data;
+ new_rdata->e_d_tov = rdata->e_d_tov;
+ new_rdata->r_a_tov = rdata->r_a_tov;
+ new_rdata->ops = rdata->ops;
+ new_rdata->local_port = rdata->local_port;
+ new_rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
+ new_rdata->trans_state = FC_PORTSTATE_REAL;
+ mutex_init(&new_rdata->rp_mutex);
+ INIT_DELAYED_WORK(&new_rdata->retry_work,
+ fc_rport_timeout);
+ INIT_LIST_HEAD(&new_rdata->peers);
+ INIT_WORK(&new_rdata->event_work, fc_rport_work);
+
+ fc_rport_state_enter(new_rport, RPORT_ST_READY);
+ } else {
+ FC_DBG("Failed to create the rport for port "
+ "(%6x).\n", ids.port_id);
+ event = RPORT_EV_FAILED;
+ }
+ put_device(&rport->dev);
+ rport = new_rport;
+ rdata = new_rport->dd_data;
+ if (rport_ops->event_callback)
+ rport_ops->event_callback(lport, rport, event);
+ } else if ((event == RPORT_EV_FAILED) ||
+ (event == RPORT_EV_LOGO) ||
+ (event == RPORT_EV_STOP)) {
+ trans_state = rdata->trans_state;
+ mutex_unlock(&rdata->rp_mutex);
+ if (rport_ops->event_callback)
+ rport_ops->event_callback(lport, rport, event);
+ if (trans_state == FC_PORTSTATE_ROGUE)
+ put_device(&rport->dev);
+ else
+ fc_remote_port_delete(rport);
+ } else
+ mutex_unlock(&rdata->rp_mutex);
+}
+
+/**
+ * fc_rport_login - Start the remote port login state machine
+ * @rport: Fibre Channel remote port
+ *
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
+ */
+int fc_rport_login(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_DEBUG_RPORT("Login to port (%6x)\n", rport->port_id);
+
+ fc_rport_enter_plogi(rport);
+
+ mutex_unlock(&rdata->rp_mutex);
+
+ return 0;
+}
+
+/**
+ * fc_rport_logoff - Logoff and remove an rport
+ * @rport: Fibre Channel remote port to be removed
+ *
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
+ */
+int fc_rport_logoff(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_DEBUG_RPORT("Remove port (%6x)\n", rport->port_id);
+
+ fc_rport_enter_logo(rport);
+
+ /*
+ * Change the state to NONE so that we discard
+ * the response.
+ */
+ fc_rport_state_enter(rport, RPORT_ST_NONE);
+
+ mutex_unlock(&rdata->rp_mutex);
+
+ cancel_delayed_work_sync(&rdata->retry_work);
+
+ mutex_lock(&rdata->rp_mutex);
+
+ rdata->event = RPORT_EV_STOP;
+ queue_work(rport_event_queue, &rdata->event_work);
+
+ mutex_unlock(&rdata->rp_mutex);
+
+ return 0;
+}
+
+/**
+ * fc_rport_enter_ready - The rport is ready
+ * @rport: Fibre Channel remote port that is ready
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_ready(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+
+ fc_rport_state_enter(rport, RPORT_ST_READY);
+
+ FC_DEBUG_RPORT("Port (%6x) is Ready\n", rport->port_id);
+
+ rdata->event = RPORT_EV_CREATED;
+ queue_work(rport_event_queue, &rdata->event_work);
+}
+
+/**
+ * fc_rport_timeout - Handler for the retry_work timer.
+ * @work: The work struct of the fc_rport_libfc_priv
+ *
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
+ */
+static void fc_rport_timeout(struct work_struct *work)
+{
+ struct fc_rport_libfc_priv *rdata =
+ container_of(work, struct fc_rport_libfc_priv, retry_work.work);
+ struct fc_rport *rport = PRIV_TO_RPORT(rdata);
+
+ mutex_lock(&rdata->rp_mutex);
+
+ switch (rdata->rp_state) {
+ case RPORT_ST_PLOGI:
+ fc_rport_enter_plogi(rport);
+ break;
+ case RPORT_ST_PRLI:
+ fc_rport_enter_prli(rport);
+ break;
+ case RPORT_ST_RTV:
+ fc_rport_enter_rtv(rport);
+ break;
+ case RPORT_ST_LOGO:
+ fc_rport_enter_logo(rport);
+ break;
+ case RPORT_ST_READY:
+ case RPORT_ST_INIT:
+ case RPORT_ST_NONE:
+ break;
+ }
+
+ mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
+}
+
+/**
+ * fc_rport_error - Handler for any errors
+ * @rport: The fc_rport object
+ * @fp: The frame pointer
+ *
+ * If the error was caused by a resource allocation failure
+ * then wait for half a second and retry, otherwise retry
+ * immediately.
+ *
+ * Locking Note: The rport lock is expected to be held before
+ * calling this routine
+ */
+static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ unsigned long delay = 0;
+
+ FC_DEBUG_RPORT("Error %ld in state %s, retries %d\n",
+ PTR_ERR(fp), fc_rport_state(rport), rdata->retries);
+
+ if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
+ /*
+ * Memory allocation failure, or the exchange timed out.
+ * Retry after delay
+ */
+ if (rdata->retries < rdata->local_port->max_retry_count) {
+ rdata->retries++;
+ if (!fp)
+ delay = msecs_to_jiffies(500);
+ get_device(&rport->dev);
+ schedule_delayed_work(&rdata->retry_work, delay);
+ } else {
+ switch (rdata->rp_state) {
+ case RPORT_ST_PLOGI:
+ case RPORT_ST_PRLI:
+ case RPORT_ST_LOGO:
+ rdata->event = RPORT_EV_FAILED;
+ queue_work(rport_event_queue,
+ &rdata->event_work);
+ break;
+ case RPORT_ST_RTV:
+ fc_rport_enter_ready(rport);
+ break;
+ case RPORT_ST_NONE:
+ case RPORT_ST_READY:
+ case RPORT_ST_INIT:
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * fc_rport_plogi_recv_resp - Handle incoming ELS PLOGI response
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: response frame
+ * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
+ */
+static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rp_arg)
+{
+ struct fc_rport *rport = rp_arg;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_els_flogi *plp;
+ unsigned int tov;
+ u16 csp_seq;
+ u16 cssp_seq;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_DEBUG_RPORT("Received a PLOGI response from port (%6x)\n",
+ rport->port_id);
+
+ if (rdata->rp_state != RPORT_ST_PLOGI) {
+ FC_DBG("Received a PLOGI response, but in state %s\n",
+ fc_rport_state(rport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_rport_error(rport, fp);
+ goto err;
+ }
+
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC &&
+ (plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
+ rport->port_name = get_unaligned_be64(&plp->fl_wwpn);
+ rport->node_name = get_unaligned_be64(&plp->fl_wwnn);
+
+ tov = ntohl(plp->fl_csp.sp_e_d_tov);
+ if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
+ tov /= 1000;
+ if (tov > rdata->e_d_tov)
+ rdata->e_d_tov = tov;
+ csp_seq = ntohs(plp->fl_csp.sp_tot_seq);
+ cssp_seq = ntohs(plp->fl_cssp[3 - 1].cp_con_seq);
+ if (cssp_seq < csp_seq)
+ csp_seq = cssp_seq;
+ rdata->max_seq = csp_seq;
+ rport->maxframe_size =
+ fc_plogi_get_maxframe(plp, lport->mfs);
+
+ /*
+ * If the rport is one of the well known addresses
+ * we skip PRLI and RTV and go straight to READY.
+ */
+ if (rport->port_id >= FC_FID_DOM_MGR)
+ fc_rport_enter_ready(rport);
+ else
+ fc_rport_enter_prli(rport);
+ } else
+ fc_rport_error(rport, fp);
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
+}
+
+/**
+ * fc_rport_enter_plogi - Send Port Login (PLOGI) request to peer
+ * @rport: Fibre Channel remote port to send PLOGI to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_plogi(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+
+ FC_DEBUG_RPORT("Port (%6x) entered PLOGI state from %s state\n",
+ rport->port_id, fc_rport_state(rport));
+
+ fc_rport_state_enter(rport, RPORT_ST_PLOGI);
+
+ rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
+ if (!fp) {
+ fc_rport_error(rport, fp);
+ return;
+ }
+ rdata->e_d_tov = lport->e_d_tov;
+
+ if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI,
+ fc_rport_plogi_resp, rport, lport->e_d_tov))
+ fc_rport_error(rport, fp);
+ else
+ get_device(&rport->dev);
+}
+
+/**
+ * fc_rport_prli_resp - Process Login (PRLI) response handler
+ * @sp: current sequence in the PRLI exchange
+ * @fp: response frame
+ * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
+ */
+static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rp_arg)
+{
+ struct fc_rport *rport = rp_arg;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp spp;
+ } *pp;
+ u32 roles = FC_RPORT_ROLE_UNKNOWN;
+ u32 fcp_parm = 0;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_DEBUG_RPORT("Received a PRLI response from port (%6x)\n",
+ rport->port_id);
+
+ if (rdata->rp_state != RPORT_ST_PRLI) {
+ FC_DBG("Received a PRLI response, but in state %s\n",
+ fc_rport_state(rport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_rport_error(rport, fp);
+ goto err;
+ }
+
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC) {
+ pp = fc_frame_payload_get(fp, sizeof(*pp));
+ if (pp && pp->prli.prli_spp_len >= sizeof(pp->spp)) {
+ fcp_parm = ntohl(pp->spp.spp_params);
+ if (fcp_parm & FCP_SPPF_RETRY)
+ rdata->flags |= FC_RP_FLAGS_RETRY;
+ }
+
+ rport->supported_classes = FC_COS_CLASS3;
+ if (fcp_parm & FCP_SPPF_INIT_FCN)
+ roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ if (fcp_parm & FCP_SPPF_TARG_FCN)
+ roles |= FC_RPORT_ROLE_FCP_TARGET;
+
+ rport->roles = roles;
+ fc_rport_enter_rtv(rport);
+
+ } else {
+ FC_DBG("Bad ELS response\n");
+ rdata->event = RPORT_EV_FAILED;
+ queue_work(rport_event_queue, &rdata->event_work);
+ }
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
+}
+
+/**
+ * fc_rport_logo_resp - Logout (LOGO) response handler
+ * @sp: current sequence in the LOGO exchange
+ * @fp: response frame
+ * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
+ */
+static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rp_arg)
+{
+ struct fc_rport *rport = rp_arg;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_DEBUG_RPORT("Received a LOGO response from port (%6x)\n",
+ rport->port_id);
+
+ if (IS_ERR(fp)) {
+ fc_rport_error(rport, fp);
+ goto err;
+ }
+
+ if (rdata->rp_state != RPORT_ST_LOGO) {
+ FC_DEBUG_RPORT("Received a LOGO response, but in state %s\n",
+ fc_rport_state(rport));
+ goto out;
+ }
+
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC) {
+ fc_rport_enter_rtv(rport);
+ } else {
+ FC_DBG("Bad ELS response\n");
+ rdata->event = RPORT_EV_LOGO;
+ queue_work(rport_event_queue, &rdata->event_work);
+ }
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
+}
+
+/**
+ * fc_rport_enter_prli - Send Process Login (PRLI) request to peer
+ * @rport: Fibre Channel remote port to send PRLI to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_prli(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp spp;
+ } *pp;
+ struct fc_frame *fp;
+
+ FC_DEBUG_RPORT("Port (%6x) entered PRLI state from %s state\n",
+ rport->port_id, fc_rport_state(rport));
+
+ fc_rport_state_enter(rport, RPORT_ST_PRLI);
+
+ fp = fc_frame_alloc(lport, sizeof(*pp));
+ if (!fp) {
+ fc_rport_error(rport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI,
+ fc_rport_prli_resp, rport, lport->e_d_tov))
+ fc_rport_error(rport, fp);
+ else
+ get_device(&rport->dev);
+}
+
+/**
+ * fc_rport_els_rtv_resp - Request Timeout Value response handler
+ * @sp: current sequence in the RTV exchange
+ * @fp: response frame
+ * @rp_arg: Fibre Channel remote port
+ *
+ * Many targets don't seem to support this.
+ *
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
+ */
+static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rp_arg)
+{
+ struct fc_rport *rport = rp_arg;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_DEBUG_RPORT("Received a RTV response from port (%6x)\n",
+ rport->port_id);
+
+ if (rdata->rp_state != RPORT_ST_RTV) {
+ FC_DBG("Received a RTV response, but in state %s\n",
+ fc_rport_state(rport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_rport_error(rport, fp);
+ goto err;
+ }
+
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC) {
+ struct fc_els_rtv_acc *rtv;
+ u32 toq;
+ u32 tov;
+
+ rtv = fc_frame_payload_get(fp, sizeof(*rtv));
+ if (rtv) {
+ toq = ntohl(rtv->rtv_toq);
+ tov = ntohl(rtv->rtv_r_a_tov);
+ if (tov == 0)
+ tov = 1;
+ rdata->r_a_tov = tov;
+ tov = ntohl(rtv->rtv_e_d_tov);
+ if (toq & FC_ELS_RTV_EDRES)
+ tov /= 1000000;
+ if (tov == 0)
+ tov = 1;
+ rdata->e_d_tov = tov;
+ }
+ }
+
+ fc_rport_enter_ready(rport);
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
+}
+
+/**
+ * fc_rport_enter_rtv - Send Request Timeout Value (RTV) request to peer
+ * @rport: Fibre Channel remote port to send RTV to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_rtv(struct fc_rport *rport)
+{
+ struct fc_frame *fp;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+
+ FC_DEBUG_RPORT("Port (%6x) entered RTV state from %s state\n",
+ rport->port_id, fc_rport_state(rport));
+
+ fc_rport_state_enter(rport, RPORT_ST_RTV);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv));
+ if (!fp) {
+ fc_rport_error(rport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV,
+ fc_rport_rtv_resp, rport, lport->e_d_tov))
+ fc_rport_error(rport, fp);
+ else
+ get_device(&rport->dev);
+}
+
+/**
+ * fc_rport_enter_logo - Send Logout (LOGO) request to peer
+ * @rport: Fibre Channel remote port to send LOGO to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_logo(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+
+ FC_DEBUG_RPORT("Port (%6x) entered LOGO state from %s state\n",
+ rport->port_id, fc_rport_state(rport));
+
+ fc_rport_state_enter(rport, RPORT_ST_LOGO);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo));
+ if (!fp) {
+ fc_rport_error(rport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO,
+ fc_rport_logo_resp, rport, lport->e_d_tov))
+ fc_rport_error(rport, fp);
+ else
+ get_device(&rport->dev);
+}
+
+
+/**
+ * fc_rport_recv_req - Receive a request from a rport
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: response frame
+ * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
+ */
+void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+
+ struct fc_frame_header *fh;
+ struct fc_seq_els_data els_data;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ els_data.fp = NULL;
+ els_data.explan = ELS_EXPL_NONE;
+ els_data.reason = ELS_RJT_NONE;
+
+ fh = fc_frame_header_get(fp);
+
+ if (fh->fh_r_ctl == FC_RCTL_ELS_REQ && fh->fh_type == FC_TYPE_ELS) {
+ op = fc_frame_payload_op(fp);
+ switch (op) {
+ case ELS_PLOGI:
+ fc_rport_recv_plogi_req(rport, sp, fp);
+ break;
+ case ELS_PRLI:
+ fc_rport_recv_prli_req(rport, sp, fp);
+ break;
+ case ELS_PRLO:
+ fc_rport_recv_prlo_req(rport, sp, fp);
+ break;
+ case ELS_LOGO:
+ fc_rport_recv_logo_req(rport, sp, fp);
+ break;
+ case ELS_RRQ:
+ els_data.fp = fp;
+ lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data);
+ break;
+ case ELS_REC:
+ els_data.fp = fp;
+ lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
+ break;
+ default:
+ els_data.reason = ELS_RJT_UNSUP;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
+ break;
+ }
+ }
+
+ mutex_unlock(&rdata->rp_mutex);
+}
+
+/**
+ * fc_rport_recv_plogi_req - Handle incoming Port Login (PLOGI) request
+ * @rport: Fibre Channel remote port that initiated PLOGI
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: PLOGI request frame
+ *
+ * Locking Note: The rport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_rport_recv_plogi_req(struct fc_rport *rport,
+ struct fc_seq *sp, struct fc_frame *rx_fp)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp = rx_fp;
+ struct fc_exch *ep;
+ struct fc_frame_header *fh;
+ struct fc_els_flogi *pl;
+ struct fc_seq_els_data rjt_data;
+ u32 sid;
+ u64 wwpn;
+ u64 wwnn;
+ enum fc_els_rjt_reason reject = 0;
+ u32 f_ctl;
+ rjt_data.fp = NULL;
+
+ fh = fc_frame_header_get(fp);
+
+ FC_DEBUG_RPORT("Received PLOGI request from port (%6x) "
+ "while in state %s\n", ntoh24(fh->fh_s_id),
+ fc_rport_state(rport));
+
+ sid = ntoh24(fh->fh_s_id);
+ pl = fc_frame_payload_get(fp, sizeof(*pl));
+ if (!pl) {
+ FC_DBG("incoming PLOGI from %x too short\n", sid);
+ WARN_ON(1);
+ /* XXX TBD: send reject? */
+ fc_frame_free(fp);
+ return;
+ }
+ wwpn = get_unaligned_be64(&pl->fl_wwpn);
+ wwnn = get_unaligned_be64(&pl->fl_wwnn);
+
+ /*
+ * If the session was just created, possibly due to the incoming PLOGI,
+ * set the state appropriately and accept the PLOGI.
+ *
+ * If we had also sent a PLOGI, and if the received PLOGI is from a
+ * higher WWPN, we accept it, otherwise an LS_RJT is sent with reason
+ * "command already in progress".
+ *
+ * XXX TBD: If the session was ready before, the PLOGI should result in
+ * all outstanding exchanges being reset.
+ */
+ switch (rdata->rp_state) {
+ case RPORT_ST_INIT:
+ FC_DEBUG_RPORT("incoming PLOGI from %6x wwpn %llx state INIT "
+ "- reject\n", sid, wwpn);
+ reject = ELS_RJT_UNSUP;
+ break;
+ case RPORT_ST_PLOGI:
+ FC_DEBUG_RPORT("incoming PLOGI from %x in PLOGI state %d\n",
+ sid, rdata->rp_state);
+ if (wwpn < lport->wwpn)
+ reject = ELS_RJT_INPROG;
+ break;
+ case RPORT_ST_PRLI:
+ case RPORT_ST_READY:
+ FC_DEBUG_RPORT("incoming PLOGI from %x in logged-in state %d "
+ "- ignored for now\n", sid, rdata->rp_state);
+ /* XXX TBD - should reset */
+ break;
+ case RPORT_ST_NONE:
+ default:
+ FC_DEBUG_RPORT("incoming PLOGI from %x in unexpected "
+ "state %d\n", sid, rdata->rp_state);
+ break;
+ }
+
+ if (reject) {
+ rjt_data.reason = reject;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ fc_frame_free(fp);
+ } else {
+ fp = fc_frame_alloc(lport, sizeof(*pl));
+ if (fp == NULL) {
+ fp = rx_fp;
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ fc_frame_free(fp);
+ } else {
+ sp = lport->tt.seq_start_next(sp);
+ WARN_ON(!sp);
+ fc_rport_set_name(rport, wwpn, wwnn);
+
+ /*
+ * Get session payload size from incoming PLOGI.
+ */
+ rport->maxframe_size =
+ fc_plogi_get_maxframe(pl, lport->mfs);
+ fc_frame_free(rx_fp);
+ fc_plogi_fill(lport, fp, ELS_LS_ACC);
+
+ /*
+ * Send LS_ACC. If this fails,
+ * the originator should retry.
+ */
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+ f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ ep = fc_seq_exch(sp);
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+ if (rdata->rp_state == RPORT_ST_PLOGI)
+ fc_rport_enter_prli(rport);
+ }
+ }
+}
+
+/**
+ * fc_rport_recv_prli_req - Handle incoming Process Login (PRLI) request
+ * @rport: Fibre Channel remote port that initiated PRLI
+ * @sp: current sequence in the PRLI exchange
+ * @fp: PRLI request frame
+ *
+ * Locking Note: The rport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_rport_recv_prli_req(struct fc_rport *rport,
+ struct fc_seq *sp, struct fc_frame *rx_fp)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_exch *ep;
+ struct fc_frame *fp;
+ struct fc_frame_header *fh;
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp spp;
+ } *pp;
+ struct fc_els_spp *rspp; /* request service param page */
+ struct fc_els_spp *spp; /* response spp */
+ unsigned int len;
+ unsigned int plen;
+ enum fc_els_rjt_reason reason = ELS_RJT_UNAB;
+ enum fc_els_rjt_explan explan = ELS_EXPL_NONE;
+ enum fc_els_spp_resp resp;
+ struct fc_seq_els_data rjt_data;
+ u32 f_ctl;
+ u32 fcp_parm;
+ u32 roles = FC_RPORT_ROLE_UNKNOWN;
+ rjt_data.fp = NULL;
+
+ fh = fc_frame_header_get(rx_fp);
+
+ FC_DEBUG_RPORT("Received PRLI request from port (%6x) "
+ "while in state %s\n", ntoh24(fh->fh_s_id),
+ fc_rport_state(rport));
+
+ switch (rdata->rp_state) {
+ case RPORT_ST_PRLI:
+ case RPORT_ST_READY:
+ reason = ELS_RJT_NONE;
+ break;
+ default:
+ break;
+ }
+ len = fr_len(rx_fp) - sizeof(*fh);
+ pp = fc_frame_payload_get(rx_fp, sizeof(*pp));
+ if (pp == NULL) {
+ reason = ELS_RJT_PROT;
+ explan = ELS_EXPL_INV_LEN;
+ } else {
+ plen = ntohs(pp->prli.prli_len);
+ if ((plen % 4) != 0 || plen > len) {
+ reason = ELS_RJT_PROT;
+ explan = ELS_EXPL_INV_LEN;
+ } else if (plen < len) {
+ len = plen;
+ }
+ plen = pp->prli.prli_spp_len;
+ if ((plen % 4) != 0 || plen < sizeof(*spp) ||
+ plen > len || len < sizeof(*pp)) {
+ reason = ELS_RJT_PROT;
+ explan = ELS_EXPL_INV_LEN;
+ }
+ rspp = &pp->spp;
+ }
+ if (reason != ELS_RJT_NONE ||
+ (fp = fc_frame_alloc(lport, len)) == NULL) {
+ rjt_data.reason = reason;
+ rjt_data.explan = explan;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ } else {
+ sp = lport->tt.seq_start_next(sp);
+ WARN_ON(!sp);
+ pp = fc_frame_payload_get(fp, len);
+ WARN_ON(!pp);
+ memset(pp, 0, len);
+ pp->prli.prli_cmd = ELS_LS_ACC;
+ pp->prli.prli_spp_len = plen;
+ pp->prli.prli_len = htons(len);
+ len -= sizeof(struct fc_els_prli);
+
+ /*
+ * Go through all the service parameter pages and build
+ * response. If plen indicates longer SPP than standard,
+ * use that. The entire response has been pre-cleared above.
+ */
+ spp = &pp->spp;
+ while (len >= plen) {
+ spp->spp_type = rspp->spp_type;
+ spp->spp_type_ext = rspp->spp_type_ext;
+ spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
+ resp = FC_SPP_RESP_ACK;
+ if (rspp->spp_flags & FC_SPP_RPA_VAL)
+ resp = FC_SPP_RESP_NO_PA;
+ switch (rspp->spp_type) {
+ case 0: /* common to all FC-4 types */
+ break;
+ case FC_TYPE_FCP:
+ fcp_parm = ntohl(rspp->spp_params);
+ if (fcp_parm * FCP_SPPF_RETRY)
+ rdata->flags |= FC_RP_FLAGS_RETRY;
+ rport->supported_classes = FC_COS_CLASS3;
+ if (fcp_parm & FCP_SPPF_INIT_FCN)
+ roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ if (fcp_parm & FCP_SPPF_TARG_FCN)
+ roles |= FC_RPORT_ROLE_FCP_TARGET;
+ rport->roles = roles;
+
+ spp->spp_params =
+ htonl(lport->service_params);
+ break;
+ default:
+ resp = FC_SPP_RESP_INVL;
+ break;
+ }
+ spp->spp_flags |= resp;
+ len -= plen;
+ rspp = (struct fc_els_spp *)((char *)rspp + plen);
+ spp = (struct fc_els_spp *)((char *)spp + plen);
+ }
+
+ /*
+ * Send LS_ACC. If this fails, the originator should retry.
+ */
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+ f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ ep = fc_seq_exch(sp);
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+
+ /*
+ * Get lock and re-check state.
+ */
+ switch (rdata->rp_state) {
+ case RPORT_ST_PRLI:
+ fc_rport_enter_ready(rport);
+ break;
+ case RPORT_ST_READY:
+ break;
+ default:
+ break;
+ }
+ }
+ fc_frame_free(rx_fp);
+}
+
+/**
+ * fc_rport_recv_prlo_req - Handle incoming Process Logout (PRLO) request
+ * @rport: Fibre Channel remote port that initiated PRLO
+ * @sp: current sequence in the PRLO exchange
+ * @fp: PRLO request frame
+ *
+ * Locking Note: The rport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
+ struct fc_frame *fp)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+
+ struct fc_frame_header *fh;
+ struct fc_seq_els_data rjt_data;
+
+ fh = fc_frame_header_get(fp);
+
+ FC_DEBUG_RPORT("Received PRLO request from port (%6x) "
+ "while in state %s\n", ntoh24(fh->fh_s_id),
+ fc_rport_state(rport));
+
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_rport_recv_logo_req - Handle incoming Logout (LOGO) request
+ * @rport: Fibre Channel remote port that initiated LOGO
+ * @sp: current sequence in the LOGO exchange
+ * @fp: LOGO request frame
+ *
+ * Locking Note: The rport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+
+ fh = fc_frame_header_get(fp);
+
+ FC_DEBUG_RPORT("Received LOGO request from port (%6x) "
+ "while in state %s\n", ntoh24(fh->fh_s_id),
+ fc_rport_state(rport));
+
+ rdata->event = RPORT_EV_LOGO;
+ queue_work(rport_event_queue, &rdata->event_work);
+
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+ fc_frame_free(fp);
+}
+
+static void fc_rport_flush_queue(void)
+{
+ flush_workqueue(rport_event_queue);
+}
+
+
+int fc_rport_init(struct fc_lport *lport)
+{
+ if (!lport->tt.rport_login)
+ lport->tt.rport_login = fc_rport_login;
+
+ if (!lport->tt.rport_logoff)
+ lport->tt.rport_logoff = fc_rport_logoff;
+
+ if (!lport->tt.rport_recv_req)
+ lport->tt.rport_recv_req = fc_rport_recv_req;
+
+ if (!lport->tt.rport_flush_queue)
+ lport->tt.rport_flush_queue = fc_rport_flush_queue;
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_rport_init);
+
+int fc_setup_rport()
+{
+ rport_event_queue = create_singlethread_workqueue("fc_rport_eq");
+ if (!rport_event_queue)
+ return -ENOMEM;
+ return 0;
+}
+EXPORT_SYMBOL(fc_setup_rport);
+
+void fc_destroy_rport()
+{
+ destroy_workqueue(rport_event_queue);
+}
+EXPORT_SYMBOL(fc_destroy_rport);
+
+void fc_rport_terminate_io(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+
+ lport->tt.exch_mgr_reset(lport->emp, 0, rport->port_id);
+ lport->tt.exch_mgr_reset(lport->emp, rport->port_id, 0);
+}
+EXPORT_SYMBOL(fc_rport_terminate_io);
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 3fdee7370ccc..7225b6e2029e 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -88,34 +88,47 @@ iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
}
EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);
-void iscsi_prep_unsolicit_data_pdu(struct iscsi_task *task,
- struct iscsi_data *hdr)
+/**
+ * iscsi_prep_data_out_pdu - initialize Data-Out
+ * @task: scsi command task
+ * @r2t: R2T info
+ * @hdr: iscsi data in pdu
+ *
+ * Notes:
+ * Initialize Data-Out within this R2T sequence and finds
+ * proper data_offset within this SCSI command.
+ *
+ * This function is called with connection lock taken.
+ **/
+void iscsi_prep_data_out_pdu(struct iscsi_task *task, struct iscsi_r2t_info *r2t,
+ struct iscsi_data *hdr)
{
struct iscsi_conn *conn = task->conn;
+ unsigned int left = r2t->data_length - r2t->sent;
+
+ task->hdr_len = sizeof(struct iscsi_data);
memset(hdr, 0, sizeof(struct iscsi_data));
- hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
- hdr->datasn = cpu_to_be32(task->unsol_datasn);
- task->unsol_datasn++;
+ hdr->ttt = r2t->ttt;
+ hdr->datasn = cpu_to_be32(r2t->datasn);
+ r2t->datasn++;
hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
- memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
-
- hdr->itt = task->hdr->itt;
- hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
- hdr->offset = cpu_to_be32(task->unsol_offset);
-
- if (task->unsol_count > conn->max_xmit_dlength) {
+ memcpy(hdr->lun, task->lun, sizeof(hdr->lun));
+ hdr->itt = task->hdr_itt;
+ hdr->exp_statsn = r2t->exp_statsn;
+ hdr->offset = cpu_to_be32(r2t->data_offset + r2t->sent);
+ if (left > conn->max_xmit_dlength) {
hton24(hdr->dlength, conn->max_xmit_dlength);
- task->data_count = conn->max_xmit_dlength;
- task->unsol_offset += task->data_count;
+ r2t->data_count = conn->max_xmit_dlength;
hdr->flags = 0;
} else {
- hton24(hdr->dlength, task->unsol_count);
- task->data_count = task->unsol_count;
+ hton24(hdr->dlength, left);
+ r2t->data_count = left;
hdr->flags = ISCSI_FLAG_CMD_FINAL;
}
+ conn->dataout_pdus_cnt++;
}
-EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu);
+EXPORT_SYMBOL_GPL(iscsi_prep_data_out_pdu);
static int iscsi_add_hdr(struct iscsi_task *task, unsigned len)
{
@@ -206,11 +219,24 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
{
struct iscsi_conn *conn = task->conn;
struct iscsi_session *session = conn->session;
- struct iscsi_cmd *hdr = task->hdr;
struct scsi_cmnd *sc = task->sc;
+ struct iscsi_cmd *hdr;
unsigned hdrlength, cmd_len;
+ itt_t itt;
int rc;
+ rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD);
+ if (rc)
+ return rc;
+ hdr = (struct iscsi_cmd *) task->hdr;
+ itt = hdr->itt;
+ memset(hdr, 0, sizeof(*hdr));
+
+ if (session->tt->parse_pdu_itt)
+ hdr->itt = task->hdr_itt = itt;
+ else
+ hdr->itt = task->hdr_itt = build_itt(task->itt,
+ task->conn->session->age);
task->hdr_len = 0;
rc = iscsi_add_hdr(task, sizeof(*hdr));
if (rc)
@@ -218,8 +244,8 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
hdr->opcode = ISCSI_OP_SCSI_CMD;
hdr->flags = ISCSI_ATTR_SIMPLE;
int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
- hdr->itt = build_itt(task->itt, session->age);
- hdr->cmdsn = cpu_to_be32(session->cmdsn);
+ memcpy(task->lun, hdr->lun, sizeof(task->lun));
+ hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn);
session->cmdsn++;
hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
cmd_len = sc->cmd_len;
@@ -242,6 +268,8 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
}
if (sc->sc_data_direction == DMA_TO_DEVICE) {
unsigned out_len = scsi_out(sc)->length;
+ struct iscsi_r2t_info *r2t = &task->unsol_r2t;
+
hdr->data_length = cpu_to_be32(out_len);
hdr->flags |= ISCSI_FLAG_CMD_WRITE;
/*
@@ -254,13 +282,11 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
* without R2T ack right after
* immediate data
*
- * r2t_data_count bytes to be sent via R2T ack's
+ * r2t data_length bytes to be sent via R2T ack's
*
* pad_count bytes to be sent as zero-padding
*/
- task->unsol_count = 0;
- task->unsol_offset = 0;
- task->unsol_datasn = 0;
+ memset(r2t, 0, sizeof(*r2t));
if (session->imm_data_en) {
if (out_len >= session->first_burst)
@@ -274,12 +300,14 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
zero_data(hdr->dlength);
if (!session->initial_r2t_en) {
- task->unsol_count = min(session->first_burst, out_len)
- - task->imm_count;
- task->unsol_offset = task->imm_count;
+ r2t->data_length = min(session->first_burst, out_len) -
+ task->imm_count;
+ r2t->data_offset = task->imm_count;
+ r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
+ r2t->exp_statsn = cpu_to_be32(conn->exp_statsn);
}
- if (!task->unsol_count)
+ if (!task->unsol_r2t.data_length)
/* No unsolicit Data-Out's */
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
} else {
@@ -300,8 +328,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
WARN_ON(hdrlength >= 256);
hdr->hlength = hdrlength & 0xFF;
- if (conn->session->tt->init_task &&
- conn->session->tt->init_task(task))
+ if (session->tt->init_task && session->tt->init_task(task))
return -EIO;
task->state = ISCSI_TASK_RUNNING;
@@ -332,6 +359,7 @@ static void iscsi_complete_command(struct iscsi_task *task)
struct iscsi_session *session = conn->session;
struct scsi_cmnd *sc = task->sc;
+ session->tt->cleanup_task(task);
list_del_init(&task->running);
task->state = ISCSI_TASK_COMPLETED;
task->sc = NULL;
@@ -402,8 +430,6 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_task *task,
* the cmd in the sequencing
*/
conn->session->queued_cmdsn--;
- else
- conn->session->tt->cleanup_task(conn, task);
sc->result = err;
if (!scsi_bidi_cmnd(sc))
@@ -423,7 +449,7 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
struct iscsi_task *task)
{
struct iscsi_session *session = conn->session;
- struct iscsi_hdr *hdr = (struct iscsi_hdr *)task->hdr;
+ struct iscsi_hdr *hdr = task->hdr;
struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
@@ -437,7 +463,6 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
*/
nop->cmdsn = cpu_to_be32(session->cmdsn);
if (hdr->itt != RESERVED_ITT) {
- hdr->itt = build_itt(task->itt, session->age);
/*
* TODO: We always use immediate, so we never hit this.
* If we start to send tmfs or nops as non-immediate then
@@ -450,12 +475,13 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
}
}
- if (session->tt->init_task)
- session->tt->init_task(task);
+ if (session->tt->init_task && session->tt->init_task(task))
+ return -EIO;
if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
session->state = ISCSI_STATE_LOGGING_OUT;
+ task->state = ISCSI_TASK_RUNNING;
list_move_tail(&task->running, &conn->mgmt_run_list);
debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
hdr->opcode & ISCSI_OPCODE_MASK, hdr->itt,
@@ -469,6 +495,7 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
{
struct iscsi_session *session = conn->session;
struct iscsi_task *task;
+ itt_t itt;
if (session->state == ISCSI_STATE_TERMINATE)
return NULL;
@@ -505,23 +532,47 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
} else
task->data_count = 0;
+ if (conn->session->tt->alloc_pdu(task, hdr->opcode)) {
+ iscsi_conn_printk(KERN_ERR, conn, "Could not allocate "
+ "pdu for mgmt task.\n");
+ goto requeue_task;
+ }
+ itt = task->hdr->itt;
+ task->hdr_len = sizeof(struct iscsi_hdr);
memcpy(task->hdr, hdr, sizeof(struct iscsi_hdr));
+
+ if (hdr->itt != RESERVED_ITT) {
+ if (session->tt->parse_pdu_itt)
+ task->hdr->itt = itt;
+ else
+ task->hdr->itt = build_itt(task->itt,
+ task->conn->session->age);
+ }
+
INIT_LIST_HEAD(&task->running);
list_add_tail(&task->running, &conn->mgmtqueue);
if (session->tt->caps & CAP_DATA_PATH_OFFLOAD) {
- if (iscsi_prep_mgmt_task(conn, task)) {
- __iscsi_put_task(task);
- return NULL;
- }
+ if (iscsi_prep_mgmt_task(conn, task))
+ goto free_task;
if (session->tt->xmit_task(task))
- task = NULL;
+ goto free_task;
} else
scsi_queue_work(conn->session->host, &conn->xmitwork);
return task;
+
+free_task:
+ __iscsi_put_task(task);
+ return NULL;
+
+requeue_task:
+ if (task != conn->login_task)
+ __kfifo_put(session->cmdpool.queue, (void*)&task,
+ sizeof(void*));
+ return NULL;
}
int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
@@ -709,7 +760,6 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
{
struct iscsi_reject *reject = (struct iscsi_reject *)hdr;
struct iscsi_hdr rejected_pdu;
- uint32_t itt;
conn->exp_statsn = be32_to_cpu(reject->statsn) + 1;
@@ -719,10 +769,9 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {
memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
- itt = get_itt(rejected_pdu.itt);
iscsi_conn_printk(KERN_ERR, conn,
- "itt 0x%x had pdu (op 0x%x) rejected "
- "due to DataDigest error.\n", itt,
+ "pdu (op 0x%x) rejected "
+ "due to DataDigest error.\n",
rejected_pdu.opcode);
}
}
@@ -742,12 +791,15 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
static struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
{
struct iscsi_session *session = conn->session;
- uint32_t i;
+ int i;
if (itt == RESERVED_ITT)
return NULL;
- i = get_itt(itt);
+ if (session->tt->parse_pdu_itt)
+ session->tt->parse_pdu_itt(conn, itt, &i, NULL);
+ else
+ i = get_itt(itt);
if (i >= session->cmds_max)
return NULL;
@@ -922,20 +974,25 @@ EXPORT_SYMBOL_GPL(iscsi_complete_pdu);
int iscsi_verify_itt(struct iscsi_conn *conn, itt_t itt)
{
struct iscsi_session *session = conn->session;
- uint32_t i;
+ int age = 0, i = 0;
if (itt == RESERVED_ITT)
return 0;
- if (((__force u32)itt & ISCSI_AGE_MASK) !=
- (session->age << ISCSI_AGE_SHIFT)) {
+ if (session->tt->parse_pdu_itt)
+ session->tt->parse_pdu_itt(conn, itt, &i, &age);
+ else {
+ i = get_itt(itt);
+ age = ((__force u32)itt >> ISCSI_AGE_SHIFT) & ISCSI_AGE_MASK;
+ }
+
+ if (age != session->age) {
iscsi_conn_printk(KERN_ERR, conn,
"received itt %x expected session age (%x)\n",
(__force u32)itt, session->age);
return ISCSI_ERR_BAD_ITT;
}
- i = get_itt(itt);
if (i >= session->cmds_max) {
iscsi_conn_printk(KERN_ERR, conn,
"received invalid itt index %u (max cmds "
@@ -1136,8 +1193,13 @@ check_mgmt:
fail_command(conn, conn->task, DID_IMM_RETRY << 16);
continue;
}
- if (iscsi_prep_scsi_cmd_pdu(conn->task)) {
- fail_command(conn, conn->task, DID_ABORT << 16);
+ rc = iscsi_prep_scsi_cmd_pdu(conn->task);
+ if (rc) {
+ if (rc == -ENOMEM) {
+ conn->task = NULL;
+ goto again;
+ } else
+ fail_command(conn, conn->task, DID_ABORT << 16);
continue;
}
rc = iscsi_xmit_task(conn);
@@ -1195,6 +1257,26 @@ static void iscsi_xmitworker(struct work_struct *work)
} while (rc >= 0 || rc == -EAGAIN);
}
+static inline struct iscsi_task *iscsi_alloc_task(struct iscsi_conn *conn,
+ struct scsi_cmnd *sc)
+{
+ struct iscsi_task *task;
+
+ if (!__kfifo_get(conn->session->cmdpool.queue,
+ (void *) &task, sizeof(void *)))
+ return NULL;
+
+ sc->SCp.phase = conn->session->age;
+ sc->SCp.ptr = (char *) task;
+
+ atomic_set(&task->refcount, 1);
+ task->state = ISCSI_TASK_PENDING;
+ task->conn = conn;
+ task->sc = sc;
+ INIT_LIST_HEAD(&task->running);
+ return task;
+}
+
enum {
FAILURE_BAD_HOST = 1,
FAILURE_SESSION_FAILED,
@@ -1281,33 +1363,27 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
goto reject;
}
- if (!__kfifo_get(session->cmdpool.queue, (void*)&task,
- sizeof(void*))) {
+ task = iscsi_alloc_task(conn, sc);
+ if (!task) {
reason = FAILURE_OOM;
goto reject;
}
- sc->SCp.phase = session->age;
- sc->SCp.ptr = (char *)task;
-
- atomic_set(&task->refcount, 1);
- task->state = ISCSI_TASK_PENDING;
- task->conn = conn;
- task->sc = sc;
- INIT_LIST_HEAD(&task->running);
list_add_tail(&task->running, &conn->xmitqueue);
if (session->tt->caps & CAP_DATA_PATH_OFFLOAD) {
- if (iscsi_prep_scsi_cmd_pdu(task)) {
- sc->result = DID_ABORT << 16;
- sc->scsi_done = NULL;
- iscsi_complete_command(task);
- goto fault;
+ reason = iscsi_prep_scsi_cmd_pdu(task);
+ if (reason) {
+ if (reason == -ENOMEM) {
+ reason = FAILURE_OOM;
+ goto prepd_reject;
+ } else {
+ sc->result = DID_ABORT << 16;
+ goto prepd_fault;
+ }
}
if (session->tt->xmit_task(task)) {
- sc->scsi_done = NULL;
- iscsi_complete_command(task);
reason = FAILURE_SESSION_NOT_READY;
- goto reject;
+ goto prepd_reject;
}
} else
scsi_queue_work(session->host, &conn->xmitwork);
@@ -1317,12 +1393,18 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
spin_lock(host->host_lock);
return 0;
+prepd_reject:
+ sc->scsi_done = NULL;
+ iscsi_complete_command(task);
reject:
spin_unlock(&session->lock);
debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason);
spin_lock(host->host_lock);
return SCSI_MLQUEUE_TARGET_BUSY;
+prepd_fault:
+ sc->scsi_done = NULL;
+ iscsi_complete_command(task);
fault:
spin_unlock(&session->lock);
debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason);
@@ -1634,9 +1716,9 @@ static void iscsi_prep_abort_task_pdu(struct iscsi_task *task,
hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
hdr->flags = ISCSI_TM_FUNC_ABORT_TASK & ISCSI_FLAG_TM_FUNC_MASK;
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
- memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
- hdr->rtt = task->hdr->itt;
- hdr->refcmdsn = task->hdr->cmdsn;
+ memcpy(hdr->lun, task->lun, sizeof(hdr->lun));
+ hdr->rtt = task->hdr_itt;
+ hdr->refcmdsn = task->cmdsn;
}
int iscsi_eh_abort(struct scsi_cmnd *sc)
@@ -2223,7 +2305,8 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
}
spin_unlock_bh(&session->lock);
- data = kmalloc(ISCSI_DEF_MAX_RECV_SEG_LEN, GFP_KERNEL);
+ data = (char *) __get_free_pages(GFP_KERNEL,
+ get_order(ISCSI_DEF_MAX_RECV_SEG_LEN));
if (!data)
goto login_task_data_alloc_fail;
conn->login_task->data = conn->data = data;
@@ -2294,7 +2377,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
iscsi_suspend_tx(conn);
spin_lock_bh(&session->lock);
- kfree(conn->data);
+ free_pages((unsigned long) conn->data,
+ get_order(ISCSI_DEF_MAX_RECV_SEG_LEN));
kfree(conn->persistent_address);
__kfifo_put(session->cmdpool.queue, (void*)&conn->login_task,
sizeof(void*));
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
new file mode 100644
index 000000000000..a745f91d2928
--- /dev/null
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -0,0 +1,1163 @@
+/*
+ * iSCSI over TCP/IP Data-Path lib
+ *
+ * Copyright (C) 2004 Dmitry Yusupov
+ * Copyright (C) 2004 Alex Aizman
+ * Copyright (C) 2005 - 2006 Mike Christie
+ * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
+ * maintained by open-iscsi@googlegroups.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * See the file COPYING included with this distribution for more details.
+ *
+ * Credits:
+ * Christoph Hellwig
+ * FUJITA Tomonori
+ * Arne Redlich
+ * Zhenyu Wang
+ */
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/inet.h>
+#include <linux/file.h>
+#include <linux/blkdev.h>
+#include <linux/crypto.h>
+#include <linux/delay.h>
+#include <linux/kfifo.h>
+#include <linux/scatterlist.h>
+#include <net/tcp.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_transport_iscsi.h>
+
+#include "iscsi_tcp.h"
+
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, "
+ "Dmitry Yusupov <dmitry_yus@yahoo.com>, "
+ "Alex Aizman <itn780@yahoo.com>");
+MODULE_DESCRIPTION("iSCSI/TCP data-path");
+MODULE_LICENSE("GPL");
+#undef DEBUG_TCP
+
+#ifdef DEBUG_TCP
+#define debug_tcp(fmt...) printk(KERN_INFO "tcp: " fmt)
+#else
+#define debug_tcp(fmt...)
+#endif
+
+static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment);
+
+/*
+ * Scatterlist handling: inside the iscsi_segment, we
+ * remember an index into the scatterlist, and set data/size
+ * to the current scatterlist entry. For highmem pages, we
+ * kmap as needed.
+ *
+ * Note that the page is unmapped when we return from
+ * TCP's data_ready handler, so we may end up mapping and
+ * unmapping the same page repeatedly. The whole reason
+ * for this is that we shouldn't keep the page mapped
+ * outside the softirq.
+ */
+
+/**
+ * iscsi_tcp_segment_init_sg - init indicated scatterlist entry
+ * @segment: the buffer object
+ * @sg: scatterlist
+ * @offset: byte offset into that sg entry
+ *
+ * This function sets up the segment so that subsequent
+ * data is copied to the indicated sg entry, at the given
+ * offset.
+ */
+static inline void
+iscsi_tcp_segment_init_sg(struct iscsi_segment *segment,
+ struct scatterlist *sg, unsigned int offset)
+{
+ segment->sg = sg;
+ segment->sg_offset = offset;
+ segment->size = min(sg->length - offset,
+ segment->total_size - segment->total_copied);
+ segment->data = NULL;
+}
+
+/**
+ * iscsi_tcp_segment_map - map the current S/G page
+ * @segment: iscsi_segment
+ * @recv: 1 if called from recv path
+ *
+ * We only need to possibly kmap data if scatter lists are being used,
+ * because the iscsi passthrough and internal IO paths will never use high
+ * mem pages.
+ */
+static void iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv)
+{
+ struct scatterlist *sg;
+
+ if (segment->data != NULL || !segment->sg)
+ return;
+
+ sg = segment->sg;
+ BUG_ON(segment->sg_mapped);
+ BUG_ON(sg->length == 0);
+
+ /*
+ * If the page count is greater than one it is ok to send
+ * to the network layer's zero copy send path. If not we
+ * have to go the slow sendmsg path. We always map for the
+ * recv path.
+ */
+ if (page_count(sg_page(sg)) >= 1 && !recv)
+ return;
+
+ debug_tcp("iscsi_tcp_segment_map %s %p\n", recv ? "recv" : "xmit",
+ segment);
+ segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0);
+ segment->data = segment->sg_mapped + sg->offset + segment->sg_offset;
+}
+
+void iscsi_tcp_segment_unmap(struct iscsi_segment *segment)
+{
+ debug_tcp("iscsi_tcp_segment_unmap %p\n", segment);
+
+ if (segment->sg_mapped) {
+ debug_tcp("iscsi_tcp_segment_unmap valid\n");
+ kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0);
+ segment->sg_mapped = NULL;
+ segment->data = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_segment_unmap);
+
+/*
+ * Splice the digest buffer into the buffer
+ */
+static inline void
+iscsi_tcp_segment_splice_digest(struct iscsi_segment *segment, void *digest)
+{
+ segment->data = digest;
+ segment->digest_len = ISCSI_DIGEST_SIZE;
+ segment->total_size += ISCSI_DIGEST_SIZE;
+ segment->size = ISCSI_DIGEST_SIZE;
+ segment->copied = 0;
+ segment->sg = NULL;
+ segment->hash = NULL;
+}
+
+/**
+ * iscsi_tcp_segment_done - check whether the segment is complete
+ * @tcp_conn: iscsi tcp connection
+ * @segment: iscsi segment to check
+ * @recv: set to one of this is called from the recv path
+ * @copied: number of bytes copied
+ *
+ * Check if we're done receiving this segment. If the receive
+ * buffer is full but we expect more data, move on to the
+ * next entry in the scatterlist.
+ *
+ * If the amount of data we received isn't a multiple of 4,
+ * we will transparently receive the pad bytes, too.
+ *
+ * This function must be re-entrant.
+ */
+int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment, int recv,
+ unsigned copied)
+{
+ static unsigned char padbuf[ISCSI_PAD_LEN];
+ struct scatterlist sg;
+ unsigned int pad;
+
+ debug_tcp("copied %u %u size %u %s\n", segment->copied, copied,
+ segment->size, recv ? "recv" : "xmit");
+ if (segment->hash && copied) {
+ /*
+ * If a segment is kmapd we must unmap it before sending
+ * to the crypto layer since that will try to kmap it again.
+ */
+ iscsi_tcp_segment_unmap(segment);
+
+ if (!segment->data) {
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, sg_page(segment->sg), copied,
+ segment->copied + segment->sg_offset +
+ segment->sg->offset);
+ } else
+ sg_init_one(&sg, segment->data + segment->copied,
+ copied);
+ crypto_hash_update(segment->hash, &sg, copied);
+ }
+
+ segment->copied += copied;
+ if (segment->copied < segment->size) {
+ iscsi_tcp_segment_map(segment, recv);
+ return 0;
+ }
+
+ segment->total_copied += segment->copied;
+ segment->copied = 0;
+ segment->size = 0;
+
+ /* Unmap the current scatterlist page, if there is one. */
+ iscsi_tcp_segment_unmap(segment);
+
+ /* Do we have more scatterlist entries? */
+ debug_tcp("total copied %u total size %u\n", segment->total_copied,
+ segment->total_size);
+ if (segment->total_copied < segment->total_size) {
+ /* Proceed to the next entry in the scatterlist. */
+ iscsi_tcp_segment_init_sg(segment, sg_next(segment->sg),
+ 0);
+ iscsi_tcp_segment_map(segment, recv);
+ BUG_ON(segment->size == 0);
+ return 0;
+ }
+
+ /* Do we need to handle padding? */
+ if (!(tcp_conn->iscsi_conn->session->tt->caps & CAP_PADDING_OFFLOAD)) {
+ pad = iscsi_padding(segment->total_copied);
+ if (pad != 0) {
+ debug_tcp("consume %d pad bytes\n", pad);
+ segment->total_size += pad;
+ segment->size = pad;
+ segment->data = padbuf;
+ return 0;
+ }
+ }
+
+ /*
+ * Set us up for transferring the data digest. hdr digest
+ * is completely handled in hdr done function.
+ */
+ if (segment->hash) {
+ crypto_hash_final(segment->hash, segment->digest);
+ iscsi_tcp_segment_splice_digest(segment,
+ recv ? segment->recv_digest : segment->digest);
+ return 0;
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_segment_done);
+
+/**
+ * iscsi_tcp_segment_recv - copy data to segment
+ * @tcp_conn: the iSCSI TCP connection
+ * @segment: the buffer to copy to
+ * @ptr: data pointer
+ * @len: amount of data available
+ *
+ * This function copies up to @len bytes to the
+ * given buffer, and returns the number of bytes
+ * consumed, which can actually be less than @len.
+ *
+ * If hash digest is enabled, the function will update the
+ * hash while copying.
+ * Combining these two operations doesn't buy us a lot (yet),
+ * but in the future we could implement combined copy+crc,
+ * just way we do for network layer checksums.
+ */
+static int
+iscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment, const void *ptr,
+ unsigned int len)
+{
+ unsigned int copy = 0, copied = 0;
+
+ while (!iscsi_tcp_segment_done(tcp_conn, segment, 1, copy)) {
+ if (copied == len) {
+ debug_tcp("iscsi_tcp_segment_recv copied %d bytes\n",
+ len);
+ break;
+ }
+
+ copy = min(len - copied, segment->size - segment->copied);
+ debug_tcp("iscsi_tcp_segment_recv copying %d\n", copy);
+ memcpy(segment->data + segment->copied, ptr + copied, copy);
+ copied += copy;
+ }
+ return copied;
+}
+
+inline void
+iscsi_tcp_dgst_header(struct hash_desc *hash, const void *hdr, size_t hdrlen,
+ unsigned char digest[ISCSI_DIGEST_SIZE])
+{
+ struct scatterlist sg;
+
+ sg_init_one(&sg, hdr, hdrlen);
+ crypto_hash_digest(hash, &sg, hdrlen, digest);
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_dgst_header);
+
+static inline int
+iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment)
+{
+ if (!segment->digest_len)
+ return 1;
+
+ if (memcmp(segment->recv_digest, segment->digest,
+ segment->digest_len)) {
+ debug_scsi("digest mismatch\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Helper function to set up segment buffer
+ */
+static inline void
+__iscsi_segment_init(struct iscsi_segment *segment, size_t size,
+ iscsi_segment_done_fn_t *done, struct hash_desc *hash)
+{
+ memset(segment, 0, sizeof(*segment));
+ segment->total_size = size;
+ segment->done = done;
+
+ if (hash) {
+ segment->hash = hash;
+ crypto_hash_init(hash);
+ }
+}
+
+inline void
+iscsi_segment_init_linear(struct iscsi_segment *segment, void *data,
+ size_t size, iscsi_segment_done_fn_t *done,
+ struct hash_desc *hash)
+{
+ __iscsi_segment_init(segment, size, done, hash);
+ segment->data = data;
+ segment->size = size;
+}
+EXPORT_SYMBOL_GPL(iscsi_segment_init_linear);
+
+inline int
+iscsi_segment_seek_sg(struct iscsi_segment *segment,
+ struct scatterlist *sg_list, unsigned int sg_count,
+ unsigned int offset, size_t size,
+ iscsi_segment_done_fn_t *done, struct hash_desc *hash)
+{
+ struct scatterlist *sg;
+ unsigned int i;
+
+ debug_scsi("iscsi_segment_seek_sg offset %u size %llu\n",
+ offset, size);
+ __iscsi_segment_init(segment, size, done, hash);
+ for_each_sg(sg_list, sg, sg_count, i) {
+ debug_scsi("sg %d, len %u offset %u\n", i, sg->length,
+ sg->offset);
+ if (offset < sg->length) {
+ iscsi_tcp_segment_init_sg(segment, sg, offset);
+ return 0;
+ }
+ offset -= sg->length;
+ }
+
+ return ISCSI_ERR_DATA_OFFSET;
+}
+EXPORT_SYMBOL_GPL(iscsi_segment_seek_sg);
+
+/**
+ * iscsi_tcp_hdr_recv_prep - prep segment for hdr reception
+ * @tcp_conn: iscsi connection to prep for
+ *
+ * This function always passes NULL for the hash argument, because when this
+ * function is called we do not yet know the final size of the header and want
+ * to delay the digest processing until we know that.
+ */
+void iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn)
+{
+ debug_tcp("iscsi_tcp_hdr_recv_prep(%p%s)\n", tcp_conn,
+ tcp_conn->iscsi_conn->hdrdgst_en ? ", digest enabled" : "");
+ iscsi_segment_init_linear(&tcp_conn->in.segment,
+ tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr),
+ iscsi_tcp_hdr_recv_done, NULL);
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_hdr_recv_prep);
+
+/*
+ * Handle incoming reply to any other type of command
+ */
+static int
+iscsi_tcp_data_recv_done(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment)
+{
+ struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+ int rc = 0;
+
+ if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+ return ISCSI_ERR_DATA_DGST;
+
+ rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr,
+ conn->data, tcp_conn->in.datalen);
+ if (rc)
+ return rc;
+
+ iscsi_tcp_hdr_recv_prep(tcp_conn);
+ return 0;
+}
+
+static void
+iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
+{
+ struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+ struct hash_desc *rx_hash = NULL;
+
+ if (conn->datadgst_en &
+ !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD))
+ rx_hash = tcp_conn->rx_hash;
+
+ iscsi_segment_init_linear(&tcp_conn->in.segment,
+ conn->data, tcp_conn->in.datalen,
+ iscsi_tcp_data_recv_done, rx_hash);
+}
+
+/**
+ * iscsi_tcp_cleanup_task - free tcp_task resources
+ * @task: iscsi task
+ *
+ * must be called with session lock
+ */
+void iscsi_tcp_cleanup_task(struct iscsi_task *task)
+{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct iscsi_r2t_info *r2t;
+
+ /* nothing to do for mgmt or pending tasks */
+ if (!task->sc || task->state == ISCSI_TASK_PENDING)
+ return;
+
+ /* flush task's r2t queues */
+ while (__kfifo_get(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) {
+ __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
+ sizeof(void*));
+ debug_scsi("iscsi_tcp_cleanup_task pending r2t dropped\n");
+ }
+
+ r2t = tcp_task->r2t;
+ if (r2t != NULL) {
+ __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
+ sizeof(void*));
+ tcp_task->r2t = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_cleanup_task);
+
+/**
+ * iscsi_tcp_data_in - SCSI Data-In Response processing
+ * @conn: iscsi connection
+ * @task: scsi command task
+ */
+static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
+ int datasn = be32_to_cpu(rhdr->datasn);
+ unsigned total_in_length = scsi_in(task->sc)->length;
+
+ iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr);
+ if (tcp_conn->in.datalen == 0)
+ return 0;
+
+ if (tcp_task->exp_datasn != datasn) {
+ debug_tcp("%s: task->exp_datasn(%d) != rhdr->datasn(%d)\n",
+ __func__, tcp_task->exp_datasn, datasn);
+ return ISCSI_ERR_DATASN;
+ }
+
+ tcp_task->exp_datasn++;
+
+ tcp_task->data_offset = be32_to_cpu(rhdr->offset);
+ if (tcp_task->data_offset + tcp_conn->in.datalen > total_in_length) {
+ debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n",
+ __func__, tcp_task->data_offset,
+ tcp_conn->in.datalen, total_in_length);
+ return ISCSI_ERR_DATA_OFFSET;
+ }
+
+ conn->datain_pdus_cnt++;
+ return 0;
+}
+
+/**
+ * iscsi_tcp_r2t_rsp - iSCSI R2T Response processing
+ * @conn: iscsi connection
+ * @task: scsi command task
+ */
+static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
+{
+ struct iscsi_session *session = conn->session;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_r2t_rsp *rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr;
+ struct iscsi_r2t_info *r2t;
+ int r2tsn = be32_to_cpu(rhdr->r2tsn);
+ int rc;
+
+ if (tcp_conn->in.datalen) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "invalid R2t with datalen %d\n",
+ tcp_conn->in.datalen);
+ return ISCSI_ERR_DATALEN;
+ }
+
+ if (tcp_task->exp_datasn != r2tsn){
+ debug_tcp("%s: task->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
+ __func__, tcp_task->exp_datasn, r2tsn);
+ return ISCSI_ERR_R2TSN;
+ }
+
+ /* fill-in new R2T associated with the task */
+ iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
+
+ if (!task->sc || session->state != ISCSI_STATE_LOGGED_IN) {
+ iscsi_conn_printk(KERN_INFO, conn,
+ "dropping R2T itt %d in recovery.\n",
+ task->itt);
+ return 0;
+ }
+
+ rc = __kfifo_get(tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*));
+ if (!rc) {
+ iscsi_conn_printk(KERN_ERR, conn, "Could not allocate R2T. "
+ "Target has sent more R2Ts than it "
+ "negotiated for or driver has has leaked.\n");
+ return ISCSI_ERR_PROTO;
+ }
+
+ r2t->exp_statsn = rhdr->statsn;
+ r2t->data_length = be32_to_cpu(rhdr->data_length);
+ if (r2t->data_length == 0) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "invalid R2T with zero data len\n");
+ __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
+ sizeof(void*));
+ return ISCSI_ERR_DATALEN;
+ }
+
+ if (r2t->data_length > session->max_burst)
+ debug_scsi("invalid R2T with data len %u and max burst %u."
+ "Attempting to execute request.\n",
+ r2t->data_length, session->max_burst);
+
+ r2t->data_offset = be32_to_cpu(rhdr->data_offset);
+ if (r2t->data_offset + r2t->data_length > scsi_out(task->sc)->length) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "invalid R2T with data len %u at offset %u "
+ "and total length %d\n", r2t->data_length,
+ r2t->data_offset, scsi_out(task->sc)->length);
+ __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
+ sizeof(void*));
+ return ISCSI_ERR_DATALEN;
+ }
+
+ r2t->ttt = rhdr->ttt; /* no flip */
+ r2t->datasn = 0;
+ r2t->sent = 0;
+
+ tcp_task->exp_datasn = r2tsn + 1;
+ __kfifo_put(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*));
+ conn->r2t_pdus_cnt++;
+
+ iscsi_requeue_task(task);
+ return 0;
+}
+
+/*
+ * Handle incoming reply to DataIn command
+ */
+static int
+iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment)
+{
+ struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+ struct iscsi_hdr *hdr = tcp_conn->in.hdr;
+ int rc;
+
+ if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+ return ISCSI_ERR_DATA_DGST;
+
+ /* check for non-exceptional status */
+ if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+ rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0);
+ if (rc)
+ return rc;
+ }
+
+ iscsi_tcp_hdr_recv_prep(tcp_conn);
+ return 0;
+}
+
+/**
+ * iscsi_tcp_hdr_dissect - process PDU header
+ * @conn: iSCSI connection
+ * @hdr: PDU header
+ *
+ * This function analyzes the header of the PDU received,
+ * and performs several sanity checks. If the PDU is accompanied
+ * by data, the receive buffer is set up to copy the incoming data
+ * to the correct location.
+ */
+static int
+iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+{
+ int rc = 0, opcode, ahslen;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_task *task;
+
+ /* verify PDU length */
+ tcp_conn->in.datalen = ntoh24(hdr->dlength);
+ if (tcp_conn->in.datalen > conn->max_recv_dlength) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "iscsi_tcp: datalen %d > %d\n",
+ tcp_conn->in.datalen, conn->max_recv_dlength);
+ return ISCSI_ERR_DATALEN;
+ }
+
+ /* Additional header segments. So far, we don't
+ * process additional headers.
+ */
+ ahslen = hdr->hlength << 2;
+
+ opcode = hdr->opcode & ISCSI_OPCODE_MASK;
+ /* verify itt (itt encoding: age+cid+itt) */
+ rc = iscsi_verify_itt(conn, hdr->itt);
+ if (rc)
+ return rc;
+
+ debug_tcp("opcode 0x%x ahslen %d datalen %d\n",
+ opcode, ahslen, tcp_conn->in.datalen);
+
+ switch(opcode) {
+ case ISCSI_OP_SCSI_DATA_IN:
+ spin_lock(&conn->session->lock);
+ task = iscsi_itt_to_ctask(conn, hdr->itt);
+ if (!task)
+ rc = ISCSI_ERR_BAD_ITT;
+ else
+ rc = iscsi_tcp_data_in(conn, task);
+ if (rc) {
+ spin_unlock(&conn->session->lock);
+ break;
+ }
+
+ if (tcp_conn->in.datalen) {
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct hash_desc *rx_hash = NULL;
+ struct scsi_data_buffer *sdb = scsi_in(task->sc);
+
+ /*
+ * Setup copy of Data-In into the Scsi_Cmnd
+ * Scatterlist case:
+ * We set up the iscsi_segment to point to the next
+ * scatterlist entry to copy to. As we go along,
+ * we move on to the next scatterlist entry and
+ * update the digest per-entry.
+ */
+ if (conn->datadgst_en &&
+ !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD))
+ rx_hash = tcp_conn->rx_hash;
+
+ debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, "
+ "datalen=%d)\n", tcp_conn,
+ tcp_task->data_offset,
+ tcp_conn->in.datalen);
+ rc = iscsi_segment_seek_sg(&tcp_conn->in.segment,
+ sdb->table.sgl,
+ sdb->table.nents,
+ tcp_task->data_offset,
+ tcp_conn->in.datalen,
+ iscsi_tcp_process_data_in,
+ rx_hash);
+ spin_unlock(&conn->session->lock);
+ return rc;
+ }
+ rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
+ spin_unlock(&conn->session->lock);
+ break;
+ case ISCSI_OP_SCSI_CMD_RSP:
+ if (tcp_conn->in.datalen) {
+ iscsi_tcp_data_recv_prep(tcp_conn);
+ return 0;
+ }
+ rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
+ break;
+ case ISCSI_OP_R2T:
+ spin_lock(&conn->session->lock);
+ task = iscsi_itt_to_ctask(conn, hdr->itt);
+ if (!task)
+ rc = ISCSI_ERR_BAD_ITT;
+ else if (ahslen)
+ rc = ISCSI_ERR_AHSLEN;
+ else if (task->sc->sc_data_direction == DMA_TO_DEVICE)
+ rc = iscsi_tcp_r2t_rsp(conn, task);
+ else
+ rc = ISCSI_ERR_PROTO;
+ spin_unlock(&conn->session->lock);
+ break;
+ case ISCSI_OP_LOGIN_RSP:
+ case ISCSI_OP_TEXT_RSP:
+ case ISCSI_OP_REJECT:
+ case ISCSI_OP_ASYNC_EVENT:
+ /*
+ * It is possible that we could get a PDU with a buffer larger
+ * than 8K, but there are no targets that currently do this.
+ * For now we fail until we find a vendor that needs it
+ */
+ if (ISCSI_DEF_MAX_RECV_SEG_LEN < tcp_conn->in.datalen) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "iscsi_tcp: received buffer of "
+ "len %u but conn buffer is only %u "
+ "(opcode %0x)\n",
+ tcp_conn->in.datalen,
+ ISCSI_DEF_MAX_RECV_SEG_LEN, opcode);
+ rc = ISCSI_ERR_PROTO;
+ break;
+ }
+
+ /* If there's data coming in with the response,
+ * receive it to the connection's buffer.
+ */
+ if (tcp_conn->in.datalen) {
+ iscsi_tcp_data_recv_prep(tcp_conn);
+ return 0;
+ }
+ /* fall through */
+ case ISCSI_OP_LOGOUT_RSP:
+ case ISCSI_OP_NOOP_IN:
+ case ISCSI_OP_SCSI_TMFUNC_RSP:
+ rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
+ break;
+ default:
+ rc = ISCSI_ERR_BAD_OPCODE;
+ break;
+ }
+
+ if (rc == 0) {
+ /* Anything that comes with data should have
+ * been handled above. */
+ if (tcp_conn->in.datalen)
+ return ISCSI_ERR_PROTO;
+ iscsi_tcp_hdr_recv_prep(tcp_conn);
+ }
+
+ return rc;
+}
+
+/**
+ * iscsi_tcp_hdr_recv_done - process PDU header
+ *
+ * This is the callback invoked when the PDU header has
+ * been received. If the header is followed by additional
+ * header segments, we go back for more data.
+ */
+static int
+iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment)
+{
+ struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+ struct iscsi_hdr *hdr;
+
+ /* Check if there are additional header segments
+ * *prior* to computing the digest, because we
+ * may need to go back to the caller for more.
+ */
+ hdr = (struct iscsi_hdr *) tcp_conn->in.hdr_buf;
+ if (segment->copied == sizeof(struct iscsi_hdr) && hdr->hlength) {
+ /* Bump the header length - the caller will
+ * just loop around and get the AHS for us, and
+ * call again. */
+ unsigned int ahslen = hdr->hlength << 2;
+
+ /* Make sure we don't overflow */
+ if (sizeof(*hdr) + ahslen > sizeof(tcp_conn->in.hdr_buf))
+ return ISCSI_ERR_AHSLEN;
+
+ segment->total_size += ahslen;
+ segment->size += ahslen;
+ return 0;
+ }
+
+ /* We're done processing the header. See if we're doing
+ * header digests; if so, set up the recv_digest buffer
+ * and go back for more. */
+ if (conn->hdrdgst_en &&
+ !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD)) {
+ if (segment->digest_len == 0) {
+ /*
+ * Even if we offload the digest processing we
+ * splice it in so we can increment the skb/segment
+ * counters in preparation for the data segment.
+ */
+ iscsi_tcp_segment_splice_digest(segment,
+ segment->recv_digest);
+ return 0;
+ }
+
+ iscsi_tcp_dgst_header(tcp_conn->rx_hash, hdr,
+ segment->total_copied - ISCSI_DIGEST_SIZE,
+ segment->digest);
+
+ if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+ return ISCSI_ERR_HDR_DGST;
+ }
+
+ tcp_conn->in.hdr = hdr;
+ return iscsi_tcp_hdr_dissect(conn, hdr);
+}
+
+/**
+ * iscsi_tcp_recv_segment_is_hdr - tests if we are reading in a header
+ * @tcp_conn: iscsi tcp conn
+ *
+ * returns non zero if we are currently processing or setup to process
+ * a header.
+ */
+inline int iscsi_tcp_recv_segment_is_hdr(struct iscsi_tcp_conn *tcp_conn)
+{
+ return tcp_conn->in.segment.done == iscsi_tcp_hdr_recv_done;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_recv_segment_is_hdr);
+
+/**
+ * iscsi_tcp_recv_skb - Process skb
+ * @conn: iscsi connection
+ * @skb: network buffer with header and/or data segment
+ * @offset: offset in skb
+ * @offload: bool indicating if transfer was offloaded
+ *
+ * Will return status of transfer in status. And will return
+ * number of bytes copied.
+ */
+int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
+ unsigned int offset, bool offloaded, int *status)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_segment *segment = &tcp_conn->in.segment;
+ struct skb_seq_state seq;
+ unsigned int consumed = 0;
+ int rc = 0;
+
+ debug_tcp("in %d bytes\n", skb->len - offset);
+
+ if (unlikely(conn->suspend_rx)) {
+ debug_tcp("conn %d Rx suspended!\n", conn->id);
+ *status = ISCSI_TCP_SUSPENDED;
+ return 0;
+ }
+
+ if (offloaded) {
+ segment->total_copied = segment->total_size;
+ goto segment_done;
+ }
+
+ skb_prepare_seq_read(skb, offset, skb->len, &seq);
+ while (1) {
+ unsigned int avail;
+ const u8 *ptr;
+
+ avail = skb_seq_read(consumed, &ptr, &seq);
+ if (avail == 0) {
+ debug_tcp("no more data avail. Consumed %d\n",
+ consumed);
+ *status = ISCSI_TCP_SKB_DONE;
+ skb_abort_seq_read(&seq);
+ goto skb_done;
+ }
+ BUG_ON(segment->copied >= segment->size);
+
+ debug_tcp("skb %p ptr=%p avail=%u\n", skb, ptr, avail);
+ rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr, avail);
+ BUG_ON(rc == 0);
+ consumed += rc;
+
+ if (segment->total_copied >= segment->total_size) {
+ skb_abort_seq_read(&seq);
+ goto segment_done;
+ }
+ }
+
+segment_done:
+ *status = ISCSI_TCP_SEGMENT_DONE;
+ debug_tcp("segment done\n");
+ rc = segment->done(tcp_conn, segment);
+ if (rc != 0) {
+ *status = ISCSI_TCP_CONN_ERR;
+ debug_tcp("Error receiving PDU, errno=%d\n", rc);
+ iscsi_conn_failure(conn, rc);
+ return 0;
+ }
+ /* The done() functions sets up the next segment. */
+
+skb_done:
+ conn->rxdata_octets += consumed;
+ return consumed;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_recv_skb);
+
+/**
+ * iscsi_tcp_task_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
+ * @conn: iscsi connection
+ * @task: scsi command task
+ * @sc: scsi command
+ */
+int iscsi_tcp_task_init(struct iscsi_task *task)
+{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct iscsi_conn *conn = task->conn;
+ struct scsi_cmnd *sc = task->sc;
+ int err;
+
+ if (!sc) {
+ /*
+ * mgmt tasks do not have a scatterlist since they come
+ * in from the iscsi interface.
+ */
+ debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id,
+ task->itt);
+
+ return conn->session->tt->init_pdu(task, 0, task->data_count);
+ }
+
+ BUG_ON(__kfifo_len(tcp_task->r2tqueue));
+ tcp_task->exp_datasn = 0;
+
+ /* Prepare PDU, optionally w/ immediate data */
+ debug_scsi("task deq [cid %d itt 0x%x imm %d unsol %d]\n",
+ conn->id, task->itt, task->imm_count,
+ task->unsol_r2t.data_length);
+
+ err = conn->session->tt->init_pdu(task, 0, task->imm_count);
+ if (err)
+ return err;
+ task->imm_count = 0;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_task_init);
+
+static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task)
+{
+ struct iscsi_session *session = task->conn->session;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct iscsi_r2t_info *r2t = NULL;
+
+ if (iscsi_task_has_unsol_data(task))
+ r2t = &task->unsol_r2t;
+ else {
+ spin_lock_bh(&session->lock);
+ if (tcp_task->r2t) {
+ r2t = tcp_task->r2t;
+ /* Continue with this R2T? */
+ if (r2t->data_length <= r2t->sent) {
+ debug_scsi(" done with r2t %p\n", r2t);
+ __kfifo_put(tcp_task->r2tpool.queue,
+ (void *)&tcp_task->r2t,
+ sizeof(void *));
+ tcp_task->r2t = r2t = NULL;
+ }
+ }
+
+ if (r2t == NULL) {
+ __kfifo_get(tcp_task->r2tqueue,
+ (void *)&tcp_task->r2t, sizeof(void *));
+ r2t = tcp_task->r2t;
+ }
+ spin_unlock_bh(&session->lock);
+ }
+
+ return r2t;
+}
+
+/**
+ * iscsi_tcp_task_xmit - xmit normal PDU task
+ * @task: iscsi command task
+ *
+ * We're expected to return 0 when everything was transmitted succesfully,
+ * -EAGAIN if there's still data in the queue, or != 0 for any other kind
+ * of error.
+ */
+int iscsi_tcp_task_xmit(struct iscsi_task *task)
+{
+ struct iscsi_conn *conn = task->conn;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_r2t_info *r2t;
+ int rc = 0;
+
+flush:
+ /* Flush any pending data first. */
+ rc = session->tt->xmit_pdu(task);
+ if (rc < 0)
+ return rc;
+
+ /* mgmt command */
+ if (!task->sc) {
+ if (task->hdr->itt == RESERVED_ITT)
+ iscsi_put_task(task);
+ return 0;
+ }
+
+ /* Are we done already? */
+ if (task->sc->sc_data_direction != DMA_TO_DEVICE)
+ return 0;
+
+ r2t = iscsi_tcp_get_curr_r2t(task);
+ if (r2t == NULL) {
+ /* Waiting for more R2Ts to arrive. */
+ debug_tcp("no R2Ts yet\n");
+ return 0;
+ }
+
+ rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_DATA_OUT);
+ if (rc)
+ return rc;
+ iscsi_prep_data_out_pdu(task, r2t, (struct iscsi_data *) task->hdr);
+
+ debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n",
+ r2t, r2t->datasn - 1, task->hdr->itt,
+ r2t->data_offset + r2t->sent, r2t->data_count);
+
+ rc = conn->session->tt->init_pdu(task, r2t->data_offset + r2t->sent,
+ r2t->data_count);
+ if (rc)
+ return rc;
+ r2t->sent += r2t->data_count;
+ goto flush;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_task_xmit);
+
+struct iscsi_cls_conn *
+iscsi_tcp_conn_setup(struct iscsi_cls_session *cls_session, int dd_data_size,
+ uint32_t conn_idx)
+
+{
+ struct iscsi_conn *conn;
+ struct iscsi_cls_conn *cls_conn;
+ struct iscsi_tcp_conn *tcp_conn;
+
+ cls_conn = iscsi_conn_setup(cls_session, sizeof(*tcp_conn), conn_idx);
+ if (!cls_conn)
+ return NULL;
+ conn = cls_conn->dd_data;
+ /*
+ * due to strange issues with iser these are not set
+ * in iscsi_conn_setup
+ */
+ conn->max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
+
+ tcp_conn = conn->dd_data;
+ tcp_conn->iscsi_conn = conn;
+
+ tcp_conn->dd_data = kzalloc(dd_data_size, GFP_KERNEL);
+ if (!tcp_conn->dd_data) {
+ iscsi_conn_teardown(cls_conn);
+ return NULL;
+ }
+ return cls_conn;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_conn_setup);
+
+void iscsi_tcp_conn_teardown(struct iscsi_cls_conn *cls_conn)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+
+ kfree(tcp_conn->dd_data);
+ iscsi_conn_teardown(cls_conn);
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_conn_teardown);
+
+int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session)
+{
+ int i;
+ int cmd_i;
+
+ /*
+ * initialize per-task: R2T pool and xmit queue
+ */
+ for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
+ struct iscsi_task *task = session->cmds[cmd_i];
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+
+ /*
+ * pre-allocated x2 as much r2ts to handle race when
+ * target acks DataOut faster than we data_xmit() queues
+ * could replenish r2tqueue.
+ */
+
+ /* R2T pool */
+ if (iscsi_pool_init(&tcp_task->r2tpool,
+ session->max_r2t * 2, NULL,
+ sizeof(struct iscsi_r2t_info))) {
+ goto r2t_alloc_fail;
+ }
+
+ /* R2T xmit queue */
+ tcp_task->r2tqueue = kfifo_alloc(
+ session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL);
+ if (tcp_task->r2tqueue == ERR_PTR(-ENOMEM)) {
+ iscsi_pool_free(&tcp_task->r2tpool);
+ goto r2t_alloc_fail;
+ }
+ }
+
+ return 0;
+
+r2t_alloc_fail:
+ for (i = 0; i < cmd_i; i++) {
+ struct iscsi_task *task = session->cmds[i];
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+
+ kfifo_free(tcp_task->r2tqueue);
+ iscsi_pool_free(&tcp_task->r2tpool);
+ }
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_alloc);
+
+void iscsi_tcp_r2tpool_free(struct iscsi_session *session)
+{
+ int i;
+
+ for (i = 0; i < session->cmds_max; i++) {
+ struct iscsi_task *task = session->cmds[i];
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+
+ kfifo_free(tcp_task->r2tqueue);
+ iscsi_pool_free(&tcp_task->r2tpool);
+ }
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_free);
+
+void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn,
+ struct iscsi_stats *stats)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+
+ stats->txdata_octets = conn->txdata_octets;
+ stats->rxdata_octets = conn->rxdata_octets;
+ stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
+ stats->dataout_pdus = conn->dataout_pdus_cnt;
+ stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
+ stats->datain_pdus = conn->datain_pdus_cnt;
+ stats->r2t_pdus = conn->r2t_pdus_cnt;
+ stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
+ stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_conn_get_stats);
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 60a9e6e9384b..dcba267db711 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -29,8 +29,10 @@ struct lpfc_sli2_slim;
#define LPFC_MAX_NS_RETRY 3 /* Number of retry attempts to contact
the NameServer before giving up. */
#define LPFC_CMD_PER_LUN 3 /* max outstanding cmds per lun */
-#define LPFC_DEFAULT_SG_SEG_CNT 64 /* sg element count per scsi cmnd */
-#define LPFC_MAX_SG_SEG_CNT 256 /* sg element count per scsi cmnd */
+#define LPFC_DEFAULT_SG_SEG_CNT 64 /* sg element count per scsi cmnd */
+#define LPFC_DEFAULT_PROT_SG_SEG_CNT 4096 /* sg protection elements count */
+#define LPFC_MAX_SG_SEG_CNT 4096 /* sg element count per scsi cmnd */
+#define LPFC_MAX_PROT_SG_SEG_CNT 4096 /* prot sg element count per scsi cmd*/
#define LPFC_IOCB_LIST_CNT 2250 /* list of IOCBs for fast-path usage. */
#define LPFC_Q_RAMP_UP_INTERVAL 120 /* lun q_depth ramp up interval */
#define LPFC_VNAME_LEN 100 /* vport symbolic name length */
@@ -354,8 +356,6 @@ struct lpfc_vport {
uint8_t load_flag;
#define FC_LOADING 0x1 /* HBA in process of loading drvr */
#define FC_UNLOADING 0x2 /* HBA in process of unloading drvr */
- char *vname; /* Application assigned name */
-
/* Vport Config Parameters */
uint32_t cfg_scan_down;
uint32_t cfg_lun_queue_depth;
@@ -376,7 +376,7 @@ struct lpfc_vport {
struct fc_vport *fc_vport;
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct dentry *debug_disc_trc;
struct dentry *debug_nodelist;
struct dentry *vport_debugfs_root;
@@ -428,6 +428,7 @@ struct lpfc_hba {
#define LPFC_SLI3_VPORT_TEARDOWN 0x04
#define LPFC_SLI3_CRP_ENABLED 0x08
#define LPFC_SLI3_INB_ENABLED 0x10
+#define LPFC_SLI3_BG_ENABLED 0x20
uint32_t iocb_cmd_size;
uint32_t iocb_rsp_size;
@@ -501,12 +502,14 @@ struct lpfc_hba {
uint32_t cfg_poll_tmo;
uint32_t cfg_use_msi;
uint32_t cfg_sg_seg_cnt;
+ uint32_t cfg_prot_sg_seg_cnt;
uint32_t cfg_sg_dma_buf_size;
uint64_t cfg_soft_wwnn;
uint64_t cfg_soft_wwpn;
uint32_t cfg_hba_queue_depth;
uint32_t cfg_enable_hba_reset;
uint32_t cfg_enable_hba_heartbeat;
+ uint32_t cfg_enable_bg;
lpfc_vpd_t vpd; /* vital product data */
@@ -572,6 +575,9 @@ struct lpfc_hba {
uint64_t fc4InputRequests;
uint64_t fc4OutputRequests;
uint64_t fc4ControlRequests;
+ uint64_t bg_guard_err_cnt;
+ uint64_t bg_apptag_err_cnt;
+ uint64_t bg_reftag_err_cnt;
struct lpfc_sysfs_mbox sysfs_mbox;
@@ -594,6 +600,8 @@ struct lpfc_hba {
struct fc_host_statistics link_stats;
enum intr_type_t intr_type;
+ uint32_t intr_mode;
+#define LPFC_INTR_ERROR 0xFFFFFFFF
struct msix_entry msix_entries[LPFC_MSIX_VECTORS];
struct list_head port_list;
@@ -613,12 +621,14 @@ struct lpfc_hba {
unsigned long last_rsrc_error_time;
unsigned long last_ramp_down_time;
unsigned long last_ramp_up_time;
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct dentry *hba_debugfs_root;
atomic_t debugfs_vport_count;
struct dentry *debug_hbqinfo;
struct dentry *debug_dumpHostSlim;
struct dentry *debug_dumpHBASlim;
+ struct dentry *debug_dumpData; /* BlockGuard BPL*/
+ struct dentry *debug_dumpDif; /* BlockGuard BPL*/
struct dentry *debug_slow_ring_trc;
struct lpfc_debugfs_trc *slow_ring_trc;
atomic_t slow_ring_trc_cnt;
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index aa3d6277581d..40cf0f4f327f 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -96,6 +96,61 @@ lpfc_drvr_version_show(struct device *dev, struct device_attribute *attr,
return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n");
}
+static ssize_t
+lpfc_bg_info_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ if (phba->cfg_enable_bg)
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
+ return snprintf(buf, PAGE_SIZE, "BlockGuard Enabled\n");
+ else
+ return snprintf(buf, PAGE_SIZE,
+ "BlockGuard Not Supported\n");
+ else
+ return snprintf(buf, PAGE_SIZE,
+ "BlockGuard Disabled\n");
+}
+
+static ssize_t
+lpfc_bg_guard_err_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ return snprintf(buf, PAGE_SIZE, "%llu\n",
+ (unsigned long long)phba->bg_guard_err_cnt);
+}
+
+static ssize_t
+lpfc_bg_apptag_err_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ return snprintf(buf, PAGE_SIZE, "%llu\n",
+ (unsigned long long)phba->bg_apptag_err_cnt);
+}
+
+static ssize_t
+lpfc_bg_reftag_err_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ return snprintf(buf, PAGE_SIZE, "%llu\n",
+ (unsigned long long)phba->bg_reftag_err_cnt);
+}
+
/**
* lpfc_info_show: Return some pci info about the host in ascii.
* @dev: class converted to a Scsi_host structure.
@@ -1485,6 +1540,10 @@ lpfc_vport_param_store(name)\
static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
lpfc_##name##_show, lpfc_##name##_store)
+static DEVICE_ATTR(bg_info, S_IRUGO, lpfc_bg_info_show, NULL);
+static DEVICE_ATTR(bg_guard_err, S_IRUGO, lpfc_bg_guard_err_show, NULL);
+static DEVICE_ATTR(bg_apptag_err, S_IRUGO, lpfc_bg_apptag_err_show, NULL);
+static DEVICE_ATTR(bg_reftag_err, S_IRUGO, lpfc_bg_reftag_err_show, NULL);
static DEVICE_ATTR(info, S_IRUGO, lpfc_info_show, NULL);
static DEVICE_ATTR(serialnum, S_IRUGO, lpfc_serialnum_show, NULL);
static DEVICE_ATTR(modeldesc, S_IRUGO, lpfc_modeldesc_show, NULL);
@@ -1970,6 +2029,7 @@ static DEVICE_ATTR(lpfc_devloss_tmo, S_IRUGO | S_IWUSR,
# LOG_LINK_EVENT 0x10 Link events
# LOG_FCP 0x40 FCP traffic history
# LOG_NODE 0x80 Node table events
+# LOG_BG 0x200 BlockBuard events
# LOG_MISC 0x400 Miscellaneous events
# LOG_SLI 0x800 SLI events
# LOG_FCP_ERROR 0x1000 Only log FCP errors
@@ -2769,6 +2829,42 @@ LPFC_ATTR_R(enable_hba_reset, 1, 0, 1, "Enable HBA resets from the driver.");
LPFC_ATTR_R(enable_hba_heartbeat, 1, 0, 1, "Enable HBA Heartbeat.");
/*
+# lpfc_enable_bg: Enable BlockGuard (Emulex's Implementation of T10-DIF)
+# 0 = BlockGuard disabled (default)
+# 1 = BlockGuard enabled
+# Value range is [0,1]. Default value is 0.
+*/
+LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
+
+
+/*
+# lpfc_prot_mask: i
+# - Bit mask of host protection capabilities used to register with the
+# SCSI mid-layer
+# - Only meaningful if BG is turned on (lpfc_enable_bg=1).
+# - Allows you to ultimately specify which profiles to use
+# - Default will result in registering capabilities for all profiles.
+#
+*/
+unsigned int lpfc_prot_mask = SHOST_DIX_TYPE0_PROTECTION;
+
+module_param(lpfc_prot_mask, uint, 0);
+MODULE_PARM_DESC(lpfc_prot_mask, "host protection mask");
+
+/*
+# lpfc_prot_guard: i
+# - Bit mask of protection guard types to register with the SCSI mid-layer
+# - Guard types are currently either 1) IP checksum 2) T10-DIF CRC
+# - Allows you to ultimately specify which profiles to use
+# - Default will result in registering capabilities for all guard types
+#
+*/
+unsigned char lpfc_prot_guard = SHOST_DIX_GUARD_IP;
+module_param(lpfc_prot_guard, byte, 0);
+MODULE_PARM_DESC(lpfc_prot_guard, "host protection guard type");
+
+
+/*
* lpfc_sg_seg_cnt: Initial Maximum DMA Segment Count
* This value can be set to values between 64 and 256. The default value is
* 64, but may be increased to allow for larger Max I/O sizes. The scsi layer
@@ -2777,7 +2873,15 @@ LPFC_ATTR_R(enable_hba_heartbeat, 1, 0, 1, "Enable HBA Heartbeat.");
LPFC_ATTR_R(sg_seg_cnt, LPFC_DEFAULT_SG_SEG_CNT, LPFC_DEFAULT_SG_SEG_CNT,
LPFC_MAX_SG_SEG_CNT, "Max Scatter Gather Segment Count");
+LPFC_ATTR_R(prot_sg_seg_cnt, LPFC_DEFAULT_PROT_SG_SEG_CNT,
+ LPFC_DEFAULT_PROT_SG_SEG_CNT, LPFC_MAX_PROT_SG_SEG_CNT,
+ "Max Protection Scatter Gather Segment Count");
+
struct device_attribute *lpfc_hba_attrs[] = {
+ &dev_attr_bg_info,
+ &dev_attr_bg_guard_err,
+ &dev_attr_bg_apptag_err,
+ &dev_attr_bg_reftag_err,
&dev_attr_info,
&dev_attr_serialnum,
&dev_attr_modeldesc,
@@ -2825,6 +2929,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_poll,
&dev_attr_lpfc_poll_tmo,
&dev_attr_lpfc_use_msi,
+ &dev_attr_lpfc_enable_bg,
&dev_attr_lpfc_soft_wwnn,
&dev_attr_lpfc_soft_wwpn,
&dev_attr_lpfc_soft_wwn_enable,
@@ -2833,6 +2938,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_sg_seg_cnt,
&dev_attr_lpfc_max_scsicmpl_time,
&dev_attr_lpfc_stat_data_ctrl,
+ &dev_attr_lpfc_prot_sg_seg_cnt,
NULL,
};
@@ -3282,25 +3388,28 @@ lpfc_alloc_sysfs_attr(struct lpfc_vport *vport)
int error;
error = sysfs_create_bin_file(&shost->shost_dev.kobj,
- &sysfs_ctlreg_attr);
- if (error)
+ &sysfs_drvr_stat_data_attr);
+
+ /* Virtual ports do not need ctrl_reg and mbox */
+ if (error || vport->port_type == LPFC_NPIV_PORT)
goto out;
error = sysfs_create_bin_file(&shost->shost_dev.kobj,
- &sysfs_mbox_attr);
+ &sysfs_ctlreg_attr);
if (error)
- goto out_remove_ctlreg_attr;
+ goto out_remove_stat_attr;
error = sysfs_create_bin_file(&shost->shost_dev.kobj,
- &sysfs_drvr_stat_data_attr);
+ &sysfs_mbox_attr);
if (error)
- goto out_remove_mbox_attr;
+ goto out_remove_ctlreg_attr;
return 0;
-out_remove_mbox_attr:
- sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_mbox_attr);
out_remove_ctlreg_attr:
sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr);
+out_remove_stat_attr:
+ sysfs_remove_bin_file(&shost->shost_dev.kobj,
+ &sysfs_drvr_stat_data_attr);
out:
return error;
}
@@ -3315,6 +3424,9 @@ lpfc_free_sysfs_attr(struct lpfc_vport *vport)
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
sysfs_remove_bin_file(&shost->shost_dev.kobj,
&sysfs_drvr_stat_data_attr);
+ /* Virtual ports do not need ctrl_reg and mbox */
+ if (vport->port_type == LPFC_NPIV_PORT)
+ return;
sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_mbox_attr);
sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr);
}
@@ -3792,6 +3904,23 @@ lpfc_show_rport_##field (struct device *dev, \
lpfc_rport_show_function(field, format_string, sz, ) \
static FC_RPORT_ATTR(field, S_IRUGO, lpfc_show_rport_##field, NULL)
+/**
+ * lpfc_set_vport_symbolic_name: Set the vport's symbolic name.
+ * @fc_vport: The fc_vport who's symbolic name has been changed.
+ *
+ * Description:
+ * This function is called by the transport after the @fc_vport's symbolic name
+ * has been changed. This function re-registers the symbolic name with the
+ * switch to propogate the change into the fabric if the vport is active.
+ **/
+static void
+lpfc_set_vport_symbolic_name(struct fc_vport *fc_vport)
+{
+ struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
+
+ if (vport->port_state == LPFC_VPORT_READY)
+ lpfc_ns_cmd(vport, SLI_CTNS_RSPN_ID, 0, 0);
+}
struct fc_function_template lpfc_transport_functions = {
/* fixed attributes the driver supports */
@@ -3801,6 +3930,7 @@ struct fc_function_template lpfc_transport_functions = {
.show_host_supported_fc4s = 1,
.show_host_supported_speeds = 1,
.show_host_maxframe_size = 1,
+ .show_host_symbolic_name = 1,
/* dynamic attributes the driver supports */
.get_host_port_id = lpfc_get_host_port_id,
@@ -3850,6 +3980,10 @@ struct fc_function_template lpfc_transport_functions = {
.terminate_rport_io = lpfc_terminate_rport_io,
.dd_fcvport_size = sizeof(struct lpfc_vport *),
+
+ .vport_disable = lpfc_vport_disable,
+
+ .set_vport_symbolic_name = lpfc_set_vport_symbolic_name,
};
struct fc_function_template lpfc_vport_transport_functions = {
@@ -3860,6 +3994,7 @@ struct fc_function_template lpfc_vport_transport_functions = {
.show_host_supported_fc4s = 1,
.show_host_supported_speeds = 1,
.show_host_maxframe_size = 1,
+ .show_host_symbolic_name = 1,
/* dynamic attributes the driver supports */
.get_host_port_id = lpfc_get_host_port_id,
@@ -3908,6 +4043,8 @@ struct fc_function_template lpfc_vport_transport_functions = {
.terminate_rport_io = lpfc_terminate_rport_io,
.vport_disable = lpfc_vport_disable,
+
+ .set_vport_symbolic_name = lpfc_set_vport_symbolic_name,
};
/**
@@ -3930,13 +4067,12 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_use_msi_init(phba, lpfc_use_msi);
lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset);
lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat);
+ lpfc_enable_bg_init(phba, lpfc_enable_bg);
phba->cfg_poll = lpfc_poll;
phba->cfg_soft_wwnn = 0L;
phba->cfg_soft_wwpn = 0L;
lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt);
- /* Also reinitialize the host templates with new values. */
- lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
- lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+ lpfc_prot_sg_seg_cnt_init(phba, lpfc_prot_sg_seg_cnt);
/*
* Since the sg_tablesize is module parameter, the sg_dma_buf_size
* used to create the sg_dma_buf_pool must be dynamically calculated.
@@ -3945,6 +4081,17 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
sizeof(struct fcp_rsp) +
((phba->cfg_sg_seg_cnt + 2) * sizeof(struct ulp_bde64));
+
+ if (phba->cfg_enable_bg) {
+ phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SEG_CNT;
+ phba->cfg_sg_dma_buf_size +=
+ phba->cfg_prot_sg_seg_cnt * sizeof(struct ulp_bde64);
+ }
+
+ /* Also reinitialize the host templates with new values. */
+ lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+ lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+
lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth);
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 044ef4057d28..07f4976319a5 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -22,6 +22,7 @@ typedef int (*node_filter)(struct lpfc_nodelist *, void *);
struct fc_rport;
void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
+void lpfc_dump_wakeup_param(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_config_async(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
@@ -284,12 +285,24 @@ extern void lpfc_debugfs_slow_ring_trc(struct lpfc_hba *, char *, uint32_t,
uint32_t, uint32_t);
extern struct lpfc_hbq_init *lpfc_hbq_defs[];
+/* externs BlockGuard */
+extern char *_dump_buf_data;
+extern unsigned long _dump_buf_data_order;
+extern char *_dump_buf_dif;
+extern unsigned long _dump_buf_dif_order;
+extern spinlock_t _dump_buf_lock;
+extern int _dump_buf_done;
+extern spinlock_t pgcnt_lock;
+extern unsigned int pgcnt;
+extern unsigned int lpfc_prot_mask;
+extern unsigned char lpfc_prot_guard;
+
/* Interface exported by fabric iocb scheduler */
void lpfc_fabric_abort_nport(struct lpfc_nodelist *);
void lpfc_fabric_abort_hba(struct lpfc_hba *);
void lpfc_fabric_block_timeout(unsigned long);
void lpfc_unblock_fabric_iocbs(struct lpfc_hba *);
-void lpfc_adjust_queue_depth(struct lpfc_hba *);
+void lpfc_rampdown_queue_depth(struct lpfc_hba *);
void lpfc_ramp_down_queue_handler(struct lpfc_hba *);
void lpfc_ramp_up_queue_handler(struct lpfc_hba *);
void lpfc_scsi_dev_block(struct lpfc_hba *);
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 26dae8bae2d1..896c7b0351e5 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -560,18 +560,25 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
irsp->ulpStatus, irsp->un.ulpWord[4], vport->fc_ns_retry);
/* Don't bother processing response if vport is being torn down. */
- if (vport->load_flag & FC_UNLOADING)
+ if (vport->load_flag & FC_UNLOADING) {
+ if (vport->fc_flag & FC_RSCN_MODE)
+ lpfc_els_flush_rscn(vport);
goto out;
+ }
if (lpfc_els_chk_latt(vport)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0216 Link event during NS query\n");
+ if (vport->fc_flag & FC_RSCN_MODE)
+ lpfc_els_flush_rscn(vport);
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
goto out;
}
if (lpfc_error_lost_link(irsp)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0226 NS query failed due to link event\n");
+ if (vport->fc_flag & FC_RSCN_MODE)
+ lpfc_els_flush_rscn(vport);
goto out;
}
if (irsp->ulpStatus) {
@@ -587,6 +594,8 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
if (rc == 0)
goto out;
}
+ if (vport->fc_flag & FC_RSCN_MODE)
+ lpfc_els_flush_rscn(vport);
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
"0257 GID_FT Query error: 0x%x 0x%x\n",
@@ -1008,8 +1017,10 @@ lpfc_vport_symbolic_port_name(struct lpfc_vport *vport, char *symbol,
if (n < size)
n += snprintf(symbol + n, size - n, " VPort-%d", vport->vpi);
- if (n < size && vport->vname)
- n += snprintf(symbol + n, size - n, " VName-%s", vport->vname);
+ if (n < size &&
+ strlen(vport->fc_vport->symbolic_name))
+ n += snprintf(symbol + n, size - n, " VName-%s",
+ vport->fc_vport->symbolic_name);
return n;
}
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index 771920bdde44..b615eda361d5 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -46,7 +46,7 @@
#include "lpfc_compat.h"
#include "lpfc_debugfs.h"
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/**
* debugfs interface
*
@@ -618,7 +618,7 @@ inline void
lpfc_debugfs_disc_trc(struct lpfc_vport *vport, int mask, char *fmt,
uint32_t data1, uint32_t data2, uint32_t data3)
{
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_debugfs_trc *dtp;
int index;
@@ -659,7 +659,7 @@ inline void
lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
uint32_t data1, uint32_t data2, uint32_t data3)
{
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_debugfs_trc *dtp;
int index;
@@ -680,7 +680,7 @@ lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
return;
}
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/**
* lpfc_debugfs_disc_trc_open - Open the discovery trace log.
* @inode: The inode pointer that contains a vport pointer.
@@ -907,6 +907,91 @@ out:
return rc;
}
+static int
+lpfc_debugfs_dumpData_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ if (!_dump_buf_data)
+ return -EBUSY;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundry */
+ printk(KERN_ERR "BLKGRD %s: _dump_buf_data=0x%p\n",
+ __func__, _dump_buf_data);
+ debug->buffer = _dump_buf_data;
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = (1 << _dump_buf_data_order) << PAGE_SHIFT;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static int
+lpfc_debugfs_dumpDif_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ if (!_dump_buf_dif)
+ return -EBUSY;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundry */
+ printk(KERN_ERR "BLKGRD %s: _dump_buf_dif=0x%p file=%s\n", __func__,
+ _dump_buf_dif, file->f_dentry->d_name.name);
+ debug->buffer = _dump_buf_dif;
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = (1 << _dump_buf_dif_order) << PAGE_SHIFT;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static ssize_t
+lpfc_debugfs_dumpDataDif_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ /*
+ * The Data/DIF buffers only save one failing IO
+ * The write op is used as a reset mechanism after an IO has
+ * already been saved to the next one can be saved
+ */
+ spin_lock(&_dump_buf_lock);
+
+ memset((void *)_dump_buf_data, 0,
+ ((1 << PAGE_SHIFT) << _dump_buf_data_order));
+ memset((void *)_dump_buf_dif, 0,
+ ((1 << PAGE_SHIFT) << _dump_buf_dif_order));
+
+ _dump_buf_done = 0;
+
+ spin_unlock(&_dump_buf_lock);
+
+ return nbytes;
+}
+
+
+
/**
* lpfc_debugfs_nodelist_open - Open the nodelist debugfs file.
* @inode: The inode pointer that contains a vport pointer.
@@ -1035,6 +1120,17 @@ lpfc_debugfs_release(struct inode *inode, struct file *file)
return 0;
}
+static int
+lpfc_debugfs_dumpDataDif_release(struct inode *inode, struct file *file)
+{
+ struct lpfc_debug *debug = file->private_data;
+
+ debug->buffer = NULL;
+ kfree(debug);
+
+ return 0;
+}
+
#undef lpfc_debugfs_op_disc_trc
static struct file_operations lpfc_debugfs_op_disc_trc = {
.owner = THIS_MODULE,
@@ -1080,6 +1176,26 @@ static struct file_operations lpfc_debugfs_op_dumpHostSlim = {
.release = lpfc_debugfs_release,
};
+#undef lpfc_debugfs_op_dumpData
+static struct file_operations lpfc_debugfs_op_dumpData = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_dumpData_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_dumpDataDif_write,
+ .release = lpfc_debugfs_dumpDataDif_release,
+};
+
+#undef lpfc_debugfs_op_dumpDif
+static struct file_operations lpfc_debugfs_op_dumpDif = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_dumpDif_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_dumpDataDif_write,
+ .release = lpfc_debugfs_dumpDataDif_release,
+};
+
#undef lpfc_debugfs_op_slow_ring_trc
static struct file_operations lpfc_debugfs_op_slow_ring_trc = {
.owner = THIS_MODULE,
@@ -1106,7 +1222,7 @@ static atomic_t lpfc_debugfs_hba_count;
inline void
lpfc_debugfs_initialize(struct lpfc_vport *vport)
{
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_hba *phba = vport->phba;
char name[64];
uint32_t num, i;
@@ -1176,6 +1292,32 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
goto debug_failed;
}
+ /* Setup dumpData */
+ snprintf(name, sizeof(name), "dumpData");
+ phba->debug_dumpData =
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+ phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_dumpData);
+ if (!phba->debug_dumpData) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "0800 Cannot create debugfs dumpData\n");
+ goto debug_failed;
+ }
+
+ /* Setup dumpDif */
+ snprintf(name, sizeof(name), "dumpDif");
+ phba->debug_dumpDif =
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+ phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_dumpDif);
+ if (!phba->debug_dumpDif) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "0801 Cannot create debugfs dumpDif\n");
+ goto debug_failed;
+ }
+
+
+
/* Setup slow ring trace */
if (lpfc_debugfs_max_slow_ring_trc) {
num = lpfc_debugfs_max_slow_ring_trc - 1;
@@ -1305,7 +1447,7 @@ debug_failed:
inline void
lpfc_debugfs_terminate(struct lpfc_vport *vport)
{
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_hba *phba = vport->phba;
if (vport->disc_trc) {
@@ -1340,6 +1482,16 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
debugfs_remove(phba->debug_dumpHostSlim); /* HostSlim */
phba->debug_dumpHostSlim = NULL;
}
+ if (phba->debug_dumpData) {
+ debugfs_remove(phba->debug_dumpData); /* dumpData */
+ phba->debug_dumpData = NULL;
+ }
+
+ if (phba->debug_dumpDif) {
+ debugfs_remove(phba->debug_dumpDif); /* dumpDif */
+ phba->debug_dumpDif = NULL;
+ }
+
if (phba->slow_ring_trc) {
kfree(phba->slow_ring_trc);
phba->slow_ring_trc = NULL;
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h
index 31e86a55391d..03c7313a1012 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.h
+++ b/drivers/scsi/lpfc/lpfc_debugfs.h
@@ -21,7 +21,7 @@
#ifndef _H_LPFC_DEBUG_FS
#define _H_LPFC_DEBUG_FS
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_debugfs_trc {
char *fmt;
uint32_t data1;
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 630bd28fb997..a8f30bdaff69 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -221,7 +221,11 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
/* For ELS_REQUEST64_CR, use the VPI by default */
icmd->ulpContext = vport->vpi;
icmd->ulpCt_h = 0;
- icmd->ulpCt_l = 1;
+ /* The CT field must be 0=INVALID_RPI for the ECHO cmd */
+ if (elscmd == ELS_CMD_ECHO)
+ icmd->ulpCt_l = 0; /* context = invalid RPI */
+ else
+ icmd->ulpCt_l = 1; /* context = VPI */
}
bpl = (struct ulp_bde64 *) pbuflist->virt;
@@ -271,7 +275,8 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
return elsiocb;
els_iocb_free_pbuf_exit:
- lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
+ if (expectRsp)
+ lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
kfree(pbuflist);
els_iocb_free_prsp_exit:
@@ -2468,6 +2473,15 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
case IOSTAT_LOCAL_REJECT:
switch ((irsp->un.ulpWord[4] & 0xff)) {
case IOERR_LOOP_OPEN_FAILURE:
+ if (cmd == ELS_CMD_FLOGI) {
+ if (PCI_DEVICE_ID_HORNET ==
+ phba->pcidev->device) {
+ phba->fc_topology = TOPOLOGY_LOOP;
+ phba->pport->fc_myDID = 0;
+ phba->alpa_map[0] = 0;
+ phba->alpa_map[1] = 0;
+ }
+ }
if (cmd == ELS_CMD_PLOGI && cmdiocb->retry == 0)
delay = 1000;
retry = 1;
@@ -3823,27 +3837,21 @@ lpfc_rscn_payload_check(struct lpfc_vport *vport, uint32_t did)
while (payload_len) {
rscn_did.un.word = be32_to_cpu(*lp++);
payload_len -= sizeof(uint32_t);
- switch (rscn_did.un.b.resv) {
- case 0: /* Single N_Port ID effected */
+ switch (rscn_did.un.b.resv & RSCN_ADDRESS_FORMAT_MASK) {
+ case RSCN_ADDRESS_FORMAT_PORT:
if (ns_did.un.word == rscn_did.un.word)
goto return_did_out;
break;
- case 1: /* Whole N_Port Area effected */
+ case RSCN_ADDRESS_FORMAT_AREA:
if ((ns_did.un.b.domain == rscn_did.un.b.domain)
&& (ns_did.un.b.area == rscn_did.un.b.area))
goto return_did_out;
break;
- case 2: /* Whole N_Port Domain effected */
+ case RSCN_ADDRESS_FORMAT_DOMAIN:
if (ns_did.un.b.domain == rscn_did.un.b.domain)
goto return_did_out;
break;
- default:
- /* Unknown Identifier in RSCN node */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
- "0217 Unknown Identifier in "
- "RSCN payload Data: x%x\n",
- rscn_did.un.word);
- case 3: /* Whole Fabric effected */
+ case RSCN_ADDRESS_FORMAT_FABRIC:
goto return_did_out;
}
}
@@ -3887,6 +3895,49 @@ lpfc_rscn_recovery_check(struct lpfc_vport *vport)
}
/**
+ * lpfc_send_rscn_event: Send an RSCN event to management application.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ *
+ * lpfc_send_rscn_event sends an RSCN netlink event to management
+ * applications.
+ */
+static void
+lpfc_send_rscn_event(struct lpfc_vport *vport,
+ struct lpfc_iocbq *cmdiocb)
+{
+ struct lpfc_dmabuf *pcmd;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ uint32_t *payload_ptr;
+ uint32_t payload_len;
+ struct lpfc_rscn_event_header *rscn_event_data;
+
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ payload_ptr = (uint32_t *) pcmd->virt;
+ payload_len = be32_to_cpu(*payload_ptr & ~ELS_CMD_MASK);
+
+ rscn_event_data = kmalloc(sizeof(struct lpfc_rscn_event_header) +
+ payload_len, GFP_KERNEL);
+ if (!rscn_event_data) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0147 Failed to allocate memory for RSCN event\n");
+ return;
+ }
+ rscn_event_data->event_type = FC_REG_RSCN_EVENT;
+ rscn_event_data->payload_length = payload_len;
+ memcpy(rscn_event_data->rscn_payload, payload_ptr,
+ payload_len);
+
+ fc_host_post_vendor_event(shost,
+ fc_get_event_number(),
+ sizeof(struct lpfc_els_event_header) + payload_len,
+ (char *)rscn_event_data,
+ LPFC_NL_VENDOR_ID);
+
+ kfree(rscn_event_data);
+}
+
+/**
* lpfc_els_rcv_rscn: Process an unsolicited rscn iocb.
* @vport: pointer to a host virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
@@ -3933,6 +3984,10 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
"0214 RSCN received Data: x%x x%x x%x x%x\n",
vport->fc_flag, payload_len, *lp,
vport->fc_rscn_id_cnt);
+
+ /* Send an RSCN event to the management application */
+ lpfc_send_rscn_event(vport, cmdiocb);
+
for (i = 0; i < payload_len/sizeof(uint32_t); i++)
fc_host_post_event(shost, fc_get_event_number(),
FCH_EVT_RSCN, lp[i]);
@@ -4884,10 +4939,6 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
uint32_t timeout;
uint32_t remote_ID = 0xffffffff;
- /* If the timer is already canceled do nothing */
- if ((vport->work_port_events & WORKER_ELS_TMO) == 0) {
- return;
- }
spin_lock_irq(&phba->hbalock);
timeout = (uint32_t)(phba->fc_ratov << 1);
@@ -5128,7 +5179,7 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
fc_get_event_number(),
sizeof(lsrjt_event),
(char *)&lsrjt_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
return;
}
if ((rspiocbp->iocb.ulpStatus == IOSTAT_NPORT_BSY) ||
@@ -5146,7 +5197,7 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
fc_get_event_number(),
sizeof(fabric_event),
(char *)&fabric_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
return;
}
@@ -5164,32 +5215,68 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
static void
lpfc_send_els_event(struct lpfc_vport *vport,
struct lpfc_nodelist *ndlp,
- uint32_t cmd)
+ uint32_t *payload)
{
- struct lpfc_els_event_header els_data;
+ struct lpfc_els_event_header *els_data = NULL;
+ struct lpfc_logo_event *logo_data = NULL;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- els_data.event_type = FC_REG_ELS_EVENT;
- switch (cmd) {
+ if (*payload == ELS_CMD_LOGO) {
+ logo_data = kmalloc(sizeof(struct lpfc_logo_event), GFP_KERNEL);
+ if (!logo_data) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0148 Failed to allocate memory "
+ "for LOGO event\n");
+ return;
+ }
+ els_data = &logo_data->header;
+ } else {
+ els_data = kmalloc(sizeof(struct lpfc_els_event_header),
+ GFP_KERNEL);
+ if (!els_data) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0149 Failed to allocate memory "
+ "for ELS event\n");
+ return;
+ }
+ }
+ els_data->event_type = FC_REG_ELS_EVENT;
+ switch (*payload) {
case ELS_CMD_PLOGI:
- els_data.subcategory = LPFC_EVENT_PLOGI_RCV;
+ els_data->subcategory = LPFC_EVENT_PLOGI_RCV;
break;
case ELS_CMD_PRLO:
- els_data.subcategory = LPFC_EVENT_PRLO_RCV;
+ els_data->subcategory = LPFC_EVENT_PRLO_RCV;
break;
case ELS_CMD_ADISC:
- els_data.subcategory = LPFC_EVENT_ADISC_RCV;
+ els_data->subcategory = LPFC_EVENT_ADISC_RCV;
+ break;
+ case ELS_CMD_LOGO:
+ els_data->subcategory = LPFC_EVENT_LOGO_RCV;
+ /* Copy the WWPN in the LOGO payload */
+ memcpy(logo_data->logo_wwpn, &payload[2],
+ sizeof(struct lpfc_name));
break;
default:
return;
}
- memcpy(els_data.wwpn, &ndlp->nlp_portname, sizeof(struct lpfc_name));
- memcpy(els_data.wwnn, &ndlp->nlp_nodename, sizeof(struct lpfc_name));
- fc_host_post_vendor_event(shost,
- fc_get_event_number(),
- sizeof(els_data),
- (char *)&els_data,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ memcpy(els_data->wwpn, &ndlp->nlp_portname, sizeof(struct lpfc_name));
+ memcpy(els_data->wwnn, &ndlp->nlp_nodename, sizeof(struct lpfc_name));
+ if (*payload == ELS_CMD_LOGO) {
+ fc_host_post_vendor_event(shost,
+ fc_get_event_number(),
+ sizeof(struct lpfc_logo_event),
+ (char *)logo_data,
+ LPFC_NL_VENDOR_ID);
+ kfree(logo_data);
+ } else {
+ fc_host_post_vendor_event(shost,
+ fc_get_event_number(),
+ sizeof(struct lpfc_els_event_header),
+ (char *)els_data,
+ LPFC_NL_VENDOR_ID);
+ kfree(els_data);
+ }
return;
}
@@ -5296,7 +5383,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
phba->fc_stat.elsRcvPLOGI++;
ndlp = lpfc_plogi_confirm_nport(phba, payload, ndlp);
- lpfc_send_els_event(vport, ndlp, cmd);
+ lpfc_send_els_event(vport, ndlp, payload);
if (vport->port_state < LPFC_DISC_AUTH) {
if (!(phba->pport->fc_flag & FC_PT2PT) ||
(phba->pport->fc_flag & FC_PT2PT_PLOGI)) {
@@ -5334,6 +5421,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvLOGO++;
+ lpfc_send_els_event(vport, ndlp, payload);
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
break;
@@ -5346,7 +5434,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvPRLO++;
- lpfc_send_els_event(vport, ndlp, cmd);
+ lpfc_send_els_event(vport, ndlp, payload);
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
break;
@@ -5364,7 +5452,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
"RCV ADISC: did:x%x/ste:x%x flg:x%x",
did, vport->port_state, ndlp->nlp_flag);
- lpfc_send_els_event(vport, ndlp, cmd);
+ lpfc_send_els_event(vport, ndlp, payload);
phba->fc_stat.elsRcvADISC++;
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index a1a70d9ffc2a..8c64494444bf 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -350,7 +350,7 @@ lpfc_send_fastpath_evt(struct lpfc_hba *phba,
evt_data_size = sizeof(fast_evt_data->un.
read_check_error);
} else if ((evt_sub_category == LPFC_EVENT_FABRIC_BUSY) ||
- (evt_sub_category == IOSTAT_NPORT_BSY)) {
+ (evt_sub_category == LPFC_EVENT_PORT_BUSY)) {
evt_data = (char *) &fast_evt_data->un.fabric_evt;
evt_data_size = sizeof(fast_evt_data->un.fabric_evt);
} else {
@@ -387,7 +387,7 @@ lpfc_send_fastpath_evt(struct lpfc_hba *phba,
fc_get_event_number(),
evt_data_size,
evt_data,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
lpfc_free_fast_evt(phba, fast_evt_data);
return;
@@ -585,20 +585,25 @@ lpfc_do_work(void *p)
set_user_nice(current, -20);
phba->data_flags = 0;
- while (1) {
+ while (!kthread_should_stop()) {
/* wait and check worker queue activities */
rc = wait_event_interruptible(phba->work_waitq,
(test_and_clear_bit(LPFC_DATA_READY,
&phba->data_flags)
|| kthread_should_stop()));
- BUG_ON(rc);
-
- if (kthread_should_stop())
+ /* Signal wakeup shall terminate the worker thread */
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
+ "0433 Wakeup on signal: rc=x%x\n", rc);
break;
+ }
/* Attend pending lpfc data processing */
lpfc_work_done(phba);
}
+ phba->worker_thread = NULL;
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "0432 Worker thread stopped.\n");
return 0;
}
@@ -1852,6 +1857,32 @@ lpfc_disable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state,
NLP_STE_UNUSED_NODE);
}
+/**
+ * lpfc_initialize_node: Initialize all fields of node object.
+ * @vport: Pointer to Virtual Port object.
+ * @ndlp: Pointer to FC node object.
+ * @did: FC_ID of the node.
+ * This function is always called when node object need to
+ * be initialized. It initializes all the fields of the node
+ * object.
+ **/
+static inline void
+lpfc_initialize_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ uint32_t did)
+{
+ INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
+ INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
+ init_timer(&ndlp->nlp_delayfunc);
+ ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
+ ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
+ ndlp->nlp_DID = did;
+ ndlp->vport = vport;
+ ndlp->nlp_sid = NLP_NO_SID;
+ kref_init(&ndlp->kref);
+ NLP_INT_NODE_ACT(ndlp);
+ atomic_set(&ndlp->cmd_pending, 0);
+ ndlp->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
+}
struct lpfc_nodelist *
lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
@@ -1892,17 +1923,7 @@ lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
/* re-initialize ndlp except of ndlp linked list pointer */
memset((((char *)ndlp) + sizeof (struct list_head)), 0,
sizeof (struct lpfc_nodelist) - sizeof (struct list_head));
- INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
- INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
- init_timer(&ndlp->nlp_delayfunc);
- ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
- ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
- ndlp->nlp_DID = did;
- ndlp->vport = vport;
- ndlp->nlp_sid = NLP_NO_SID;
- /* ndlp management re-initialize */
- kref_init(&ndlp->kref);
- NLP_INT_NODE_ACT(ndlp);
+ lpfc_initialize_node(vport, ndlp, did);
spin_unlock_irqrestore(&phba->ndlp_lock, flags);
@@ -3116,19 +3137,9 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
uint32_t did)
{
memset(ndlp, 0, sizeof (struct lpfc_nodelist));
- INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
- INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
- init_timer(&ndlp->nlp_delayfunc);
- ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
- ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
- ndlp->nlp_DID = did;
- ndlp->vport = vport;
- ndlp->nlp_sid = NLP_NO_SID;
+
+ lpfc_initialize_node(vport, ndlp, did);
INIT_LIST_HEAD(&ndlp->nlp_listp);
- kref_init(&ndlp->kref);
- NLP_INT_NODE_ACT(ndlp);
- atomic_set(&ndlp->cmd_pending, 0);
- ndlp->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE,
"node init: did:x%x",
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index 5de5dabbbee6..4168c7b498b8 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -65,6 +65,9 @@
#define SLI3_IOCB_RSP_SIZE 64
+/* vendor ID used in SCSI netlink calls */
+#define LPFC_NL_VENDOR_ID (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX)
+
/* Common Transport structures and definitions */
union CtRevisionId {
@@ -866,6 +869,12 @@ typedef struct _D_ID { /* Structure is in Big Endian format */
} un;
} D_ID;
+#define RSCN_ADDRESS_FORMAT_PORT 0x0
+#define RSCN_ADDRESS_FORMAT_AREA 0x1
+#define RSCN_ADDRESS_FORMAT_DOMAIN 0x2
+#define RSCN_ADDRESS_FORMAT_FABRIC 0x3
+#define RSCN_ADDRESS_FORMAT_MASK 0x3
+
/*
* Structure to define all ELS Payload types
*/
@@ -1535,6 +1544,108 @@ typedef struct ULP_BDL { /* SLI-2 */
uint32_t ulpIoTag32; /* Can be used for 32 bit I/O Tag */
} ULP_BDL;
+/*
+ * BlockGuard Definitions
+ */
+
+enum lpfc_protgrp_type {
+ LPFC_PG_TYPE_INVALID = 0, /* used to indicate errors */
+ LPFC_PG_TYPE_NO_DIF, /* no DIF data pointed to by prot grp */
+ LPFC_PG_TYPE_EMBD_DIF, /* DIF is embedded (inline) with data */
+ LPFC_PG_TYPE_DIF_BUF /* DIF has its own scatter/gather list */
+};
+
+/* PDE Descriptors */
+#define LPFC_PDE1_DESCRIPTOR 0x81
+#define LPFC_PDE2_DESCRIPTOR 0x82
+#define LPFC_PDE3_DESCRIPTOR 0x83
+
+/* BlockGuard Profiles */
+enum lpfc_bg_prof_codes {
+ LPFC_PROF_INVALID,
+ LPFC_PROF_A1 = 128, /* Full Protection */
+ LPFC_PROF_A2, /* Disabled Protection Checks:A2~A4 */
+ LPFC_PROF_A3,
+ LPFC_PROF_A4,
+ LPFC_PROF_B1, /* Embedded DIFs: B1~B3 */
+ LPFC_PROF_B2,
+ LPFC_PROF_B3,
+ LPFC_PROF_C1, /* Separate DIFs: C1~C3 */
+ LPFC_PROF_C2,
+ LPFC_PROF_C3,
+ LPFC_PROF_D1, /* Full Protection */
+ LPFC_PROF_D2, /* Partial Protection & Check Disabling */
+ LPFC_PROF_D3,
+ LPFC_PROF_E1, /* E1~E4:out - check-only, in - update apptag */
+ LPFC_PROF_E2,
+ LPFC_PROF_E3,
+ LPFC_PROF_E4,
+ LPFC_PROF_F1, /* Full Translation - F1 Prot Descriptor */
+ /* F1 Translation BDE */
+ LPFC_PROF_ANT1, /* TCP checksum, DIF inline with data buffers */
+ LPFC_PROF_AST1, /* TCP checksum, DIF split from data buffer */
+ LPFC_PROF_ANT2,
+ LPFC_PROF_AST2
+};
+
+/* BlockGuard error-control defines */
+#define BG_EC_STOP_ERR 0x00
+#define BG_EC_CONT_ERR 0x01
+#define BG_EC_IGN_UNINIT_STOP_ERR 0x10
+#define BG_EC_IGN_UNINIT_CONT_ERR 0x11
+
+/* PDE (Protection Descriptor Entry) word 0 bit masks and shifts */
+#define PDE_DESC_TYPE_MASK 0xff000000
+#define PDE_DESC_TYPE_SHIFT 24
+#define PDE_BG_PROFILE_MASK 0x00ff0000
+#define PDE_BG_PROFILE_SHIFT 16
+#define PDE_BLOCK_LEN_MASK 0x0000fffc
+#define PDE_BLOCK_LEN_SHIFT 2
+#define PDE_ERR_CTRL_MASK 0x00000003
+#define PDE_ERR_CTRL_SHIFT 0
+/* PDE word 1 bit masks and shifts */
+#define PDE_APPTAG_MASK_MASK 0xffff0000
+#define PDE_APPTAG_MASK_SHIFT 16
+#define PDE_APPTAG_VAL_MASK 0x0000ffff
+#define PDE_APPTAG_VAL_SHIFT 0
+struct lpfc_pde {
+ uint32_t parms; /* bitfields of descriptor, prof, len, and ec */
+ uint32_t apptag; /* bitfields of app tag maskand app tag value */
+ uint32_t reftag; /* reference tag occupying all 32 bits */
+};
+
+/* inline function to set fields in parms of PDE */
+static inline void
+lpfc_pde_set_bg_parms(struct lpfc_pde *p, u8 desc, u8 prof, u16 len, u8 ec)
+{
+ uint32_t *wp = &p->parms;
+
+ /* spec indicates that adapter appends two 0's to length field */
+ len = len >> 2;
+
+ *wp &= 0;
+ *wp |= ((desc << PDE_DESC_TYPE_SHIFT) & PDE_DESC_TYPE_MASK);
+ *wp |= ((prof << PDE_BG_PROFILE_SHIFT) & PDE_BG_PROFILE_MASK);
+ *wp |= ((len << PDE_BLOCK_LEN_SHIFT) & PDE_BLOCK_LEN_MASK);
+ *wp |= ((ec << PDE_ERR_CTRL_SHIFT) & PDE_ERR_CTRL_MASK);
+ *wp = le32_to_cpu(*wp);
+}
+
+/* inline function to set apptag and reftag fields of PDE */
+static inline void
+lpfc_pde_set_dif_parms(struct lpfc_pde *p, u16 apptagmask, u16 apptagval,
+ u32 reftag)
+{
+ uint32_t *wp = &p->apptag;
+ *wp &= 0;
+ *wp |= ((apptagmask << PDE_APPTAG_MASK_SHIFT) & PDE_APPTAG_MASK_MASK);
+ *wp |= ((apptagval << PDE_APPTAG_VAL_SHIFT) & PDE_APPTAG_VAL_MASK);
+ *wp = le32_to_cpu(*wp);
+ wp = &p->reftag;
+ *wp = le32_to_cpu(reftag);
+}
+
+
/* Structure for MB Command LOAD_SM and DOWN_LOAD */
typedef struct {
@@ -2359,6 +2470,30 @@ typedef struct {
#define DMP_RSP_OFFSET 0x14 /* word 5 contains first word of rsp */
#define DMP_RSP_SIZE 0x6C /* maximum of 27 words of rsp data */
+#define WAKE_UP_PARMS_REGION_ID 4
+#define WAKE_UP_PARMS_WORD_SIZE 15
+
+/* Option rom version structure */
+struct prog_id {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t type;
+ uint8_t id;
+ uint32_t ver:4; /* Major Version */
+ uint32_t rev:4; /* Revision */
+ uint32_t lev:2; /* Level */
+ uint32_t dist:2; /* Dist Type */
+ uint32_t num:4; /* number after dist type */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t num:4; /* number after dist type */
+ uint32_t dist:2; /* Dist Type */
+ uint32_t lev:2; /* Level */
+ uint32_t rev:4; /* Revision */
+ uint32_t ver:4; /* Major Version */
+ uint8_t id;
+ uint8_t type;
+#endif
+};
+
/* Structure for MB Command UPDATE_CFG (0x1B) */
struct update_cfg_var {
@@ -2552,11 +2687,19 @@ typedef struct {
uint32_t pcbLow; /* bit 31:0 of memory based port config block */
uint32_t pcbHigh; /* bit 63:32 of memory based port config block */
- uint32_t hbainit[6];
+ uint32_t hbainit[5];
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t hps : 1; /* bit 31 word9 Host Pointer in slim */
+ uint32_t rsvd : 31; /* least significant 31 bits of word 9 */
+#else /* __LITTLE_ENDIAN */
+ uint32_t rsvd : 31; /* least significant 31 bits of word 9 */
+ uint32_t hps : 1; /* bit 31 word9 Host Pointer in slim */
+#endif
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd : 24; /* Reserved */
- uint32_t cmv : 1; /* Configure Max VPIs */
+ uint32_t rsvd1 : 23; /* Reserved */
+ uint32_t cbg : 1; /* Configure BlockGuard */
+ uint32_t cmv : 1; /* Configure Max VPIs */
uint32_t ccrp : 1; /* Config Command Ring Polling */
uint32_t csah : 1; /* Configure Synchronous Abort Handling */
uint32_t chbs : 1; /* Cofigure Host Backing store */
@@ -2573,10 +2716,12 @@ typedef struct {
uint32_t csah : 1; /* Configure Synchronous Abort Handling */
uint32_t ccrp : 1; /* Config Command Ring Polling */
uint32_t cmv : 1; /* Configure Max VPIs */
- uint32_t rsvd : 24; /* Reserved */
+ uint32_t cbg : 1; /* Configure BlockGuard */
+ uint32_t rsvd1 : 23; /* Reserved */
#endif
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd2 : 24; /* Reserved */
+ uint32_t rsvd2 : 23; /* Reserved */
+ uint32_t gbg : 1; /* Grant BlockGuard */
uint32_t gmv : 1; /* Grant Max VPIs */
uint32_t gcrp : 1; /* Grant Command Ring Polling */
uint32_t gsah : 1; /* Grant Synchronous Abort Handling */
@@ -2594,7 +2739,8 @@ typedef struct {
uint32_t gsah : 1; /* Grant Synchronous Abort Handling */
uint32_t gcrp : 1; /* Grant Command Ring Polling */
uint32_t gmv : 1; /* Grant Max VPIs */
- uint32_t rsvd2 : 24; /* Reserved */
+ uint32_t gbg : 1; /* Grant BlockGuard */
+ uint32_t rsvd2 : 23; /* Reserved */
#endif
#ifdef __BIG_ENDIAN_BITFIELD
@@ -3214,6 +3360,94 @@ struct que_xri64cx_ext_fields {
struct lpfc_hbq_entry buff[5];
};
+struct sli3_bg_fields {
+ uint32_t filler[6]; /* word 8-13 in IOCB */
+ uint32_t bghm; /* word 14 - BlockGuard High Water Mark */
+/* Bitfields for bgstat (BlockGuard Status - word 15 of IOCB) */
+#define BGS_BIDIR_BG_PROF_MASK 0xff000000
+#define BGS_BIDIR_BG_PROF_SHIFT 24
+#define BGS_BIDIR_ERR_COND_FLAGS_MASK 0x003f0000
+#define BGS_BIDIR_ERR_COND_SHIFT 16
+#define BGS_BG_PROFILE_MASK 0x0000ff00
+#define BGS_BG_PROFILE_SHIFT 8
+#define BGS_INVALID_PROF_MASK 0x00000020
+#define BGS_INVALID_PROF_SHIFT 5
+#define BGS_UNINIT_DIF_BLOCK_MASK 0x00000010
+#define BGS_UNINIT_DIF_BLOCK_SHIFT 4
+#define BGS_HI_WATER_MARK_PRESENT_MASK 0x00000008
+#define BGS_HI_WATER_MARK_PRESENT_SHIFT 3
+#define BGS_REFTAG_ERR_MASK 0x00000004
+#define BGS_REFTAG_ERR_SHIFT 2
+#define BGS_APPTAG_ERR_MASK 0x00000002
+#define BGS_APPTAG_ERR_SHIFT 1
+#define BGS_GUARD_ERR_MASK 0x00000001
+#define BGS_GUARD_ERR_SHIFT 0
+ uint32_t bgstat; /* word 15 - BlockGuard Status */
+};
+
+static inline uint32_t
+lpfc_bgs_get_bidir_bg_prof(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_BIDIR_BG_PROF_MASK) >>
+ BGS_BIDIR_BG_PROF_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_bidir_err_cond(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_BIDIR_ERR_COND_FLAGS_MASK) >>
+ BGS_BIDIR_ERR_COND_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_bg_prof(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_BG_PROFILE_MASK) >>
+ BGS_BG_PROFILE_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_invalid_prof(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_INVALID_PROF_MASK) >>
+ BGS_INVALID_PROF_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_uninit_dif_block(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_UNINIT_DIF_BLOCK_MASK) >>
+ BGS_UNINIT_DIF_BLOCK_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_hi_water_mark_present(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_HI_WATER_MARK_PRESENT_MASK) >>
+ BGS_HI_WATER_MARK_PRESENT_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_reftag_err(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_REFTAG_ERR_MASK) >>
+ BGS_REFTAG_ERR_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_apptag_err(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_APPTAG_ERR_MASK) >>
+ BGS_APPTAG_ERR_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_guard_err(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_GUARD_ERR_MASK) >>
+ BGS_GUARD_ERR_SHIFT;
+}
+
#define LPFC_EXT_DATA_BDE_COUNT 3
struct fcp_irw_ext {
uint32_t io_tag64_low;
@@ -3322,6 +3556,9 @@ typedef struct _IOCB { /* IOCB structure */
struct que_xri64cx_ext_fields que_xri64cx_ext_words;
struct fcp_irw_ext fcp_ext;
uint32_t sli3Words[24]; /* 96 extra bytes for SLI-3 */
+
+ /* words 8-15 for BlockGuard */
+ struct sli3_bg_fields sli3_bg;
} unsli3;
#define ulpCt_h ulpXS
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 909be3301bba..4c77038c8f1c 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -45,6 +45,12 @@
#include "lpfc_vport.h"
#include "lpfc_version.h"
+char *_dump_buf_data;
+unsigned long _dump_buf_data_order;
+char *_dump_buf_dif;
+unsigned long _dump_buf_dif_order;
+spinlock_t _dump_buf_lock;
+
static int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int);
static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);
static int lpfc_post_rcv_buf(struct lpfc_hba *);
@@ -236,6 +242,51 @@ lpfc_config_async_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
}
/**
+ * lpfc_dump_wakeup_param_cmpl: Completion handler for dump memory mailbox
+ * command used for getting wake up parameters.
+ * @phba: pointer to lpfc hba data structure.
+ * @pmboxq: pointer to the driver internal queue element for mailbox command.
+ *
+ * This is the completion handler for dump mailbox command for getting
+ * wake up parameters. When this command complete, the response contain
+ * Option rom version of the HBA. This function translate the version number
+ * into a human readable string and store it in OptionROMVersion.
+ **/
+static void
+lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
+{
+ struct prog_id *prg;
+ uint32_t prog_id_word;
+ char dist = ' ';
+ /* character array used for decoding dist type. */
+ char dist_char[] = "nabx";
+
+ if (pmboxq->mb.mbxStatus != MBX_SUCCESS) {
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return;
+ }
+
+ prg = (struct prog_id *) &prog_id_word;
+
+ /* word 7 contain option rom version */
+ prog_id_word = pmboxq->mb.un.varWords[7];
+
+ /* Decode the Option rom version word to a readable string */
+ if (prg->dist < 4)
+ dist = dist_char[prg->dist];
+
+ if ((prg->dist == 3) && (prg->num == 0))
+ sprintf(phba->OptionROMVersion, "%d.%d%d",
+ prg->ver, prg->rev, prg->lev);
+ else
+ sprintf(phba->OptionROMVersion, "%d.%d%d%c%d",
+ prg->ver, prg->rev, prg->lev,
+ dist, prg->num);
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return;
+}
+
+/**
* lpfc_config_port_post: Perform lpfc initialization after config port.
* @phba: pointer to lpfc hba data structure.
*
@@ -482,6 +533,20 @@ lpfc_config_port_post(struct lpfc_hba *phba)
rc);
mempool_free(pmb, phba->mbox_mem_pool);
}
+
+ /* Get Option rom version */
+ pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ lpfc_dump_wakeup_param(phba, pmb);
+ pmb->mbox_cmpl = lpfc_dump_wakeup_param_cmpl;
+ pmb->vport = phba->pport;
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+
+ if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0435 Adapter failed "
+ "to get Option ROM version status x%x\n.", rc);
+ mempool_free(pmb, phba->mbox_mem_pool);
+ }
+
return 0;
}
@@ -686,11 +751,6 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
return;
spin_lock_irq(&phba->pport->work_port_lock);
- /* If the timer is already canceled do nothing */
- if (!(phba->pport->work_port_events & WORKER_HB_TMO)) {
- spin_unlock_irq(&phba->pport->work_port_lock);
- return;
- }
if (time_after(phba->last_completion_time + LPFC_HB_MBOX_INTERVAL * HZ,
jiffies)) {
@@ -833,8 +893,7 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(board_event),
(char *) &board_event,
- SCSI_NL_VID_TYPE_PCI
- | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
if (phba->work_hs & HS_FFER6) {
/* Re-establishing Link */
@@ -1984,6 +2043,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
shost->max_lun = vport->cfg_max_luns;
shost->this_id = -1;
shost->max_cmd_len = 16;
+
/*
* Set initial can_queue value since 0 is no longer supported and
* scsi_add_host will fail. This will be adjusted later based on the
@@ -2042,8 +2102,6 @@ destroy_port(struct lpfc_vport *vport)
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
- kfree(vport->vname);
-
lpfc_debugfs_terminate(vport);
fc_remove_host(shost);
scsi_remove_host(shost);
@@ -2226,8 +2284,7 @@ lpfc_enable_msix(struct lpfc_hba *phba)
ARRAY_SIZE(phba->msix_entries));
if (rc) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0420 Enable MSI-X failed (%d), continuing "
- "with MSI\n", rc);
+ "0420 PCI enable MSI-X failed (%d)\n", rc);
goto msi_fail_out;
} else
for (i = 0; i < LPFC_MSIX_VECTORS; i++)
@@ -2244,9 +2301,9 @@ lpfc_enable_msix(struct lpfc_hba *phba)
rc = request_irq(phba->msix_entries[0].vector, &lpfc_sp_intr_handler,
IRQF_SHARED, LPFC_SP_DRIVER_HANDLER_NAME, phba);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0421 MSI-X slow-path request_irq failed "
- "(%d), continuing with MSI\n", rc);
+ "(%d)\n", rc);
goto msi_fail_out;
}
@@ -2255,9 +2312,9 @@ lpfc_enable_msix(struct lpfc_hba *phba)
IRQF_SHARED, LPFC_FP_DRIVER_HANDLER_NAME, phba);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0429 MSI-X fast-path request_irq failed "
- "(%d), continuing with MSI\n", rc);
+ "(%d)\n", rc);
goto irq_fail_out;
}
@@ -2278,7 +2335,7 @@ lpfc_enable_msix(struct lpfc_hba *phba)
goto mbx_fail_out;
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
"0351 Config MSI mailbox command failed, "
"mbxCmd x%x, mbxStatus x%x\n",
pmb->mb.mbxCommand, pmb->mb.mbxStatus);
@@ -2327,6 +2384,195 @@ lpfc_disable_msix(struct lpfc_hba *phba)
}
/**
+ * lpfc_enable_msi: Enable MSI interrupt mode.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the MSI interrupt mode. The kernel
+ * function pci_enable_msi() is called to enable the MSI vector. The
+ * device driver is responsible for calling the request_irq() to register
+ * MSI vector with a interrupt the handler, which is done in this function.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ */
+static int
+lpfc_enable_msi(struct lpfc_hba *phba)
+{
+ int rc;
+
+ rc = pci_enable_msi(phba->pcidev);
+ if (!rc)
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0462 PCI enable MSI mode success.\n");
+ else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0471 PCI enable MSI mode failed (%d)\n", rc);
+ return rc;
+ }
+
+ rc = request_irq(phba->pcidev->irq, lpfc_intr_handler,
+ IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+ if (rc) {
+ pci_disable_msi(phba->pcidev);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0478 MSI request_irq failed (%d)\n", rc);
+ }
+ return rc;
+}
+
+/**
+ * lpfc_disable_msi: Disable MSI interrupt mode.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable the MSI interrupt mode. The driver
+ * calls free_irq() on MSI vector it has done request_irq() on before
+ * calling pci_disable_msi(). Failure to do so results in a BUG_ON() and
+ * a device will be left with MSI enabled and leaks its vector.
+ */
+
+static void
+lpfc_disable_msi(struct lpfc_hba *phba)
+{
+ free_irq(phba->pcidev->irq, phba);
+ pci_disable_msi(phba->pcidev);
+ return;
+}
+
+/**
+ * lpfc_log_intr_mode: Log the active interrupt mode
+ * @phba: pointer to lpfc hba data structure.
+ * @intr_mode: active interrupt mode adopted.
+ *
+ * This routine it invoked to log the currently used active interrupt mode
+ * to the device.
+ */
+static void
+lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode)
+{
+ switch (intr_mode) {
+ case 0:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0470 Enable INTx interrupt mode.\n");
+ break;
+ case 1:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0481 Enabled MSI interrupt mode.\n");
+ break;
+ case 2:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0480 Enabled MSI-X interrupt mode.\n");
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0482 Illegal interrupt mode.\n");
+ break;
+ }
+ return;
+}
+
+static void
+lpfc_stop_port(struct lpfc_hba *phba)
+{
+ /* Clear all interrupt enable conditions */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ /* Clear all pending interrupts */
+ writel(0xffffffff, phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
+
+ /* Reset some HBA SLI setup states */
+ lpfc_stop_phba_timers(phba);
+ phba->pport->work_port_events = 0;
+
+ return;
+}
+
+/**
+ * lpfc_enable_intr: Enable device interrupt.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable device interrupt and associate driver's
+ * interrupt handler(s) to interrupt vector(s). Depends on the interrupt
+ * mode configured to the driver, the driver will try to fallback from the
+ * configured interrupt mode to an interrupt mode which is supported by the
+ * platform, kernel, and device in the order of: MSI-X -> MSI -> IRQ.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static uint32_t
+lpfc_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
+{
+ uint32_t intr_mode = LPFC_INTR_ERROR;
+ int retval;
+
+ if (cfg_mode == 2) {
+ /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
+ retval = lpfc_sli_config_port(phba, 3);
+ if (!retval) {
+ /* Now, try to enable MSI-X interrupt mode */
+ retval = lpfc_enable_msix(phba);
+ if (!retval) {
+ /* Indicate initialization to MSI-X mode */
+ phba->intr_type = MSIX;
+ intr_mode = 2;
+ }
+ }
+ }
+
+ /* Fallback to MSI if MSI-X initialization failed */
+ if (cfg_mode >= 1 && phba->intr_type == NONE) {
+ retval = lpfc_enable_msi(phba);
+ if (!retval) {
+ /* Indicate initialization to MSI mode */
+ phba->intr_type = MSI;
+ intr_mode = 1;
+ }
+ }
+
+ /* Fallback to INTx if both MSI-X/MSI initalization failed */
+ if (phba->intr_type == NONE) {
+ retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
+ IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+ if (!retval) {
+ /* Indicate initialization to INTx mode */
+ phba->intr_type = INTx;
+ intr_mode = 0;
+ }
+ }
+ return intr_mode;
+}
+
+/**
+ * lpfc_disable_intr: Disable device interrupt.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable device interrupt and disassociate the
+ * driver's interrupt handler(s) from interrupt vector(s). Depending on the
+ * interrupt mode, the driver will release the interrupt vector(s) for the
+ * message signaled interrupt.
+ **/
+static void
+lpfc_disable_intr(struct lpfc_hba *phba)
+{
+ /* Disable the currently initialized interrupt mode */
+ if (phba->intr_type == MSIX)
+ lpfc_disable_msix(phba);
+ else if (phba->intr_type == MSI)
+ lpfc_disable_msi(phba);
+ else if (phba->intr_type == INTx)
+ free_irq(phba->pcidev->irq, phba);
+
+ /* Reset interrupt management states */
+ phba->intr_type = NONE;
+ phba->sli.slistat.sli_intr = 0;
+
+ return;
+}
+
+/**
* lpfc_pci_probe_one: lpfc PCI probe func to register device to PCI subsystem.
* @pdev: pointer to PCI device
* @pid: pointer to PCI device identifier
@@ -2356,6 +2602,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
int error = -ENODEV, retval;
int i, hbq_count;
uint16_t iotag;
+ uint32_t cfg_mode, intr_mode;
int bars = pci_select_bars(pdev, IORESOURCE_MEM);
struct lpfc_adapter_event_header adapter_event;
@@ -2409,6 +2656,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
phba->eratt_poll.data = (unsigned long) phba;
pci_set_master(pdev);
+ pci_save_state(pdev);
pci_try_set_mwi(pdev);
if (pci_set_dma_mask(phba->pcidev, DMA_64BIT_MASK) != 0)
@@ -2557,7 +2805,6 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
lpfc_debugfs_initialize(vport);
pci_set_drvdata(pdev, shost);
- phba->intr_type = NONE;
phba->MBslimaddr = phba->slim_memmap_p;
phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET;
@@ -2565,63 +2812,58 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
phba->HSregaddr = phba->ctrl_regs_memmap_p + HS_REG_OFFSET;
phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET;
- /* Configure and enable interrupt */
- if (phba->cfg_use_msi == 2) {
- /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
- error = lpfc_sli_config_port(phba, 3);
- if (error)
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0427 Firmware not capable of SLI 3 mode.\n");
- else {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0426 Firmware capable of SLI 3 mode.\n");
- /* Now, try to enable MSI-X interrupt mode */
- error = lpfc_enable_msix(phba);
- if (!error) {
- phba->intr_type = MSIX;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0430 enable MSI-X mode.\n");
- }
- }
- }
-
- /* Fallback to MSI if MSI-X initialization failed */
- if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
- retval = pci_enable_msi(phba->pcidev);
- if (!retval) {
- phba->intr_type = MSI;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0473 enable MSI mode.\n");
- } else
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0452 enable IRQ mode.\n");
- }
-
- /* MSI-X is the only case the doesn't need to call request_irq */
- if (phba->intr_type != MSIX) {
- retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
- IRQF_SHARED, LPFC_DRIVER_NAME, phba);
- if (retval) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0451 Enable "
- "interrupt handler failed\n");
- error = retval;
- goto out_disable_msi;
- } else if (phba->intr_type != MSI)
- phba->intr_type = INTx;
- }
-
+ /* Configure sysfs attributes */
if (lpfc_alloc_sysfs_attr(vport)) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"1476 Failed to allocate sysfs attr\n");
error = -ENOMEM;
- goto out_free_irq;
+ goto out_destroy_port;
}
- if (lpfc_sli_hba_setup(phba)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1477 Failed to set up hba\n");
- error = -ENODEV;
- goto out_remove_device;
+ cfg_mode = phba->cfg_use_msi;
+ while (true) {
+ /* Configure and enable interrupt */
+ intr_mode = lpfc_enable_intr(phba, cfg_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0426 Failed to enable interrupt.\n");
+ goto out_free_sysfs_attr;
+ }
+ /* HBA SLI setup */
+ if (lpfc_sli_hba_setup(phba)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1477 Failed to set up hba\n");
+ error = -ENODEV;
+ goto out_remove_device;
+ }
+
+ /* Wait 50ms for the interrupts of previous mailbox commands */
+ msleep(50);
+ /* Check active interrupts received */
+ if (phba->sli.slistat.sli_intr > LPFC_MSIX_VECTORS) {
+ /* Log the current active interrupt mode */
+ phba->intr_mode = intr_mode;
+ lpfc_log_intr_mode(phba, intr_mode);
+ break;
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0451 Configure interrupt mode (%d) "
+ "failed active interrupt test.\n",
+ intr_mode);
+ if (intr_mode == 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0479 Failed to enable "
+ "interrupt.\n");
+ error = -ENODEV;
+ goto out_remove_device;
+ }
+ /* Stop HBA SLI setups */
+ lpfc_stop_port(phba);
+ /* Disable the current interrupt mode */
+ lpfc_disable_intr(phba);
+ /* Try next level of interrupt mode */
+ cfg_mode = --intr_mode;
+ }
}
/*
@@ -2629,6 +2871,75 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
* the value of can_queue.
*/
shost->can_queue = phba->cfg_hba_queue_depth - 10;
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
+
+ if (lpfc_prot_mask && lpfc_prot_guard) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1478 Registering BlockGuard with the "
+ "SCSI layer\n");
+
+ scsi_host_set_prot(shost, lpfc_prot_mask);
+ scsi_host_set_guard(shost, lpfc_prot_guard);
+ }
+ }
+
+ if (!_dump_buf_data) {
+ int pagecnt = 10;
+ while (pagecnt) {
+ spin_lock_init(&_dump_buf_lock);
+ _dump_buf_data =
+ (char *) __get_free_pages(GFP_KERNEL, pagecnt);
+ if (_dump_buf_data) {
+ printk(KERN_ERR "BLKGRD allocated %d pages for "
+ "_dump_buf_data at 0x%p\n",
+ (1 << pagecnt), _dump_buf_data);
+ _dump_buf_data_order = pagecnt;
+ memset(_dump_buf_data, 0, ((1 << PAGE_SHIFT)
+ << pagecnt));
+ break;
+ } else {
+ --pagecnt;
+ }
+
+ }
+
+ if (!_dump_buf_data_order)
+ printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+ "memory for hexdump\n");
+
+ } else {
+ printk(KERN_ERR "BLKGRD already allocated _dump_buf_data=0x%p"
+ "\n", _dump_buf_data);
+ }
+
+
+ if (!_dump_buf_dif) {
+ int pagecnt = 10;
+ while (pagecnt) {
+ _dump_buf_dif =
+ (char *) __get_free_pages(GFP_KERNEL, pagecnt);
+ if (_dump_buf_dif) {
+ printk(KERN_ERR "BLKGRD allocated %d pages for "
+ "_dump_buf_dif at 0x%p\n",
+ (1 << pagecnt), _dump_buf_dif);
+ _dump_buf_dif_order = pagecnt;
+ memset(_dump_buf_dif, 0, ((1 << PAGE_SHIFT)
+ << pagecnt));
+ break;
+ } else {
+ --pagecnt;
+ }
+
+ }
+
+ if (!_dump_buf_dif_order)
+ printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+ "memory for hexdump\n");
+
+ } else {
+ printk(KERN_ERR "BLKGRD already allocated _dump_buf_dif=0x%p\n",
+ _dump_buf_dif);
+ }
lpfc_host_attrib_init(shost);
@@ -2646,29 +2957,22 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(adapter_event),
(char *) &adapter_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
-
- scsi_scan_host(shost);
+ LPFC_NL_VENDOR_ID);
return 0;
out_remove_device:
- lpfc_free_sysfs_attr(vport);
spin_lock_irq(shost->host_lock);
vport->load_flag |= FC_UNLOADING;
spin_unlock_irq(shost->host_lock);
-out_free_irq:
lpfc_stop_phba_timers(phba);
phba->pport->work_port_events = 0;
-
- if (phba->intr_type == MSIX)
- lpfc_disable_msix(phba);
- else
- free_irq(phba->pcidev->irq, phba);
-
-out_disable_msi:
- if (phba->intr_type == MSI)
- pci_disable_msi(phba->pcidev);
+ lpfc_disable_intr(phba);
+ lpfc_sli_hba_down(phba);
+ lpfc_sli_brdrestart(phba);
+out_free_sysfs_attr:
+ lpfc_free_sysfs_attr(vport);
+out_destroy_port:
destroy_port(vport);
out_kthread_stop:
kthread_stop(phba->worker_thread);
@@ -2709,7 +3013,7 @@ out:
* @pdev: pointer to PCI device
*
* This routine is to be registered to the kernel's PCI subsystem. When an
- * Emulex HBA is removed from PCI bus. It perform all the necessary cleanup
+ * Emulex HBA is removed from PCI bus, it performs all the necessary cleanup
* for the HBA device to be removed from the PCI subsystem properly.
**/
static void __devexit
@@ -2717,18 +3021,27 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_vport **vports;
struct lpfc_hba *phba = vport->phba;
+ int i;
int bars = pci_select_bars(pdev, IORESOURCE_MEM);
spin_lock_irq(&phba->hbalock);
vport->load_flag |= FC_UNLOADING;
spin_unlock_irq(&phba->hbalock);
- kfree(vport->vname);
lpfc_free_sysfs_attr(vport);
kthread_stop(phba->worker_thread);
+ /* Release all the vports against this physical port */
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL)
+ for (i = 1; i <= phba->max_vpi && vports[i] != NULL; i++)
+ fc_vport_terminate(vports[i]->fc_vport);
+ lpfc_destroy_vport_work_array(phba, vports);
+
+ /* Remove FC host and then SCSI host with the physical port */
fc_remove_host(shost);
scsi_remove_host(shost);
lpfc_cleanup(vport);
@@ -2748,13 +3061,8 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
lpfc_debugfs_terminate(vport);
- if (phba->intr_type == MSIX)
- lpfc_disable_msix(phba);
- else {
- free_irq(phba->pcidev->irq, phba);
- if (phba->intr_type == MSI)
- pci_disable_msi(phba->pcidev);
- }
+ /* Disable interrupt */
+ lpfc_disable_intr(phba);
pci_set_drvdata(pdev, NULL);
scsi_host_put(shost);
@@ -2786,6 +3094,115 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
}
/**
+ * lpfc_pci_suspend_one: lpfc PCI func to suspend device for power management.
+ * @pdev: pointer to PCI device
+ * @msg: power management message
+ *
+ * This routine is to be registered to the kernel's PCI subsystem to support
+ * system Power Management (PM). When PM invokes this method, it quiesces the
+ * device by stopping the driver's worker thread for the device, turning off
+ * device's interrupt and DMA, and bring the device offline. Note that as the
+ * driver implements the minimum PM requirements to a power-aware driver's PM
+ * support for suspend/resume -- all the possible PM messages (SUSPEND,
+ * HIBERNATE, FREEZE) to the suspend() method call will be treated as SUSPEND
+ * and the driver will fully reinitialize its device during resume() method
+ * call, the driver will set device to PCI_D3hot state in PCI config space
+ * instead of setting it according to the @msg provided by the PM.
+ *
+ * Return code
+ * 0 - driver suspended the device
+ * Error otherwise
+ **/
+static int
+lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0473 PCI device Power Management suspend.\n");
+
+ /* Bring down the device */
+ lpfc_offline_prep(phba);
+ lpfc_offline(phba);
+ kthread_stop(phba->worker_thread);
+
+ /* Disable interrupt from device */
+ lpfc_disable_intr(phba);
+
+ /* Save device state to PCI config space */
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
+}
+
+/**
+ * lpfc_pci_resume_one: lpfc PCI func to resume device for power management.
+ * @pdev: pointer to PCI device
+ *
+ * This routine is to be registered to the kernel's PCI subsystem to support
+ * system Power Management (PM). When PM invokes this method, it restores
+ * the device's PCI config space state and fully reinitializes the device
+ * and brings it online. Note that as the driver implements the minimum PM
+ * requirements to a power-aware driver's PM for suspend/resume -- all
+ * the possible PM messages (SUSPEND, HIBERNATE, FREEZE) to the suspend()
+ * method call will be treated as SUSPEND and the driver will fully
+ * reinitialize its device during resume() method call, the device will be
+ * set to PCI_D0 directly in PCI config space before restoring the state.
+ *
+ * Return code
+ * 0 - driver suspended the device
+ * Error otherwise
+ **/
+static int
+lpfc_pci_resume_one(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ uint32_t intr_mode;
+ int error;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0452 PCI device Power Management resume.\n");
+
+ /* Restore device state from PCI config space */
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ if (pdev->is_busmaster)
+ pci_set_master(pdev);
+
+ /* Startup the kernel thread for this host adapter. */
+ phba->worker_thread = kthread_run(lpfc_do_work, phba,
+ "lpfc_worker_%d", phba->brd_no);
+ if (IS_ERR(phba->worker_thread)) {
+ error = PTR_ERR(phba->worker_thread);
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0434 PM resume failed to start worker "
+ "thread: error=x%x.\n", error);
+ return error;
+ }
+
+ /* Configure and enable interrupt */
+ intr_mode = lpfc_enable_intr(phba, phba->intr_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0430 PM resume Failed to enable interrupt\n");
+ return -EIO;
+ } else
+ phba->intr_mode = intr_mode;
+
+ /* Restart HBA and bring it online */
+ lpfc_sli_brdrestart(phba);
+ lpfc_online(phba);
+
+ /* Log the current active interrupt mode */
+ lpfc_log_intr_mode(phba, phba->intr_mode);
+
+ return 0;
+}
+
+/**
* lpfc_io_error_detected: Driver method for handling PCI I/O error detected.
* @pdev: pointer to PCI device.
* @state: the current PCI connection state.
@@ -2828,13 +3245,8 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev,
pring = &psli->ring[psli->fcp_ring];
lpfc_sli_abort_iocb_ring(phba, pring);
- if (phba->intr_type == MSIX)
- lpfc_disable_msix(phba);
- else {
- free_irq(phba->pcidev->irq, phba);
- if (phba->intr_type == MSI)
- pci_disable_msi(phba->pcidev);
- }
+ /* Disable interrupt */
+ lpfc_disable_intr(phba);
/* Request a slot reset. */
return PCI_ERS_RESULT_NEED_RESET;
@@ -2862,7 +3274,7 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
struct lpfc_sli *psli = &phba->sli;
- int error, retval;
+ uint32_t intr_mode;
dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n");
if (pci_enable_device_mem(pdev)) {
@@ -2871,61 +3283,31 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
return PCI_ERS_RESULT_DISCONNECT;
}
- pci_set_master(pdev);
+ pci_restore_state(pdev);
+ if (pdev->is_busmaster)
+ pci_set_master(pdev);
spin_lock_irq(&phba->hbalock);
psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
spin_unlock_irq(&phba->hbalock);
- /* Enable configured interrupt method */
- phba->intr_type = NONE;
- if (phba->cfg_use_msi == 2) {
- /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
- error = lpfc_sli_config_port(phba, 3);
- if (error)
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0478 Firmware not capable of SLI 3 mode.\n");
- else {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0479 Firmware capable of SLI 3 mode.\n");
- /* Now, try to enable MSI-X interrupt mode */
- error = lpfc_enable_msix(phba);
- if (!error) {
- phba->intr_type = MSIX;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0480 enable MSI-X mode.\n");
- }
- }
- }
-
- /* Fallback to MSI if MSI-X initialization failed */
- if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
- retval = pci_enable_msi(phba->pcidev);
- if (!retval) {
- phba->intr_type = MSI;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0481 enable MSI mode.\n");
- } else
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0470 enable IRQ mode.\n");
- }
-
- /* MSI-X is the only case the doesn't need to call request_irq */
- if (phba->intr_type != MSIX) {
- retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
- IRQF_SHARED, LPFC_DRIVER_NAME, phba);
- if (retval) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0471 Enable interrupt handler "
- "failed\n");
- } else if (phba->intr_type != MSI)
- phba->intr_type = INTx;
- }
+ /* Configure and enable interrupt */
+ intr_mode = lpfc_enable_intr(phba, phba->intr_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0427 Cannot re-enable interrupt after "
+ "slot reset.\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ } else
+ phba->intr_mode = intr_mode;
/* Take device offline; this will perform cleanup */
lpfc_offline(phba);
lpfc_sli_brdrestart(phba);
+ /* Log the current active interrupt mode */
+ lpfc_log_intr_mode(phba, phba->intr_mode);
+
return PCI_ERS_RESULT_RECOVERED;
}
@@ -3037,6 +3419,8 @@ static struct pci_driver lpfc_driver = {
.id_table = lpfc_id_table,
.probe = lpfc_pci_probe_one,
.remove = __devexit_p(lpfc_pci_remove_one),
+ .suspend = lpfc_pci_suspend_one,
+ .resume = lpfc_pci_resume_one,
.err_handler = &lpfc_err_handler,
};
@@ -3100,6 +3484,19 @@ lpfc_exit(void)
fc_release_transport(lpfc_transport_template);
if (lpfc_enable_npiv)
fc_release_transport(lpfc_vport_transport_template);
+ if (_dump_buf_data) {
+ printk(KERN_ERR "BLKGRD freeing %lu pages for _dump_buf_data "
+ "at 0x%p\n",
+ (1L << _dump_buf_data_order), _dump_buf_data);
+ free_pages((unsigned long)_dump_buf_data, _dump_buf_data_order);
+ }
+
+ if (_dump_buf_dif) {
+ printk(KERN_ERR "BLKGRD freeing %lu pages for _dump_buf_dif "
+ "at 0x%p\n",
+ (1L << _dump_buf_dif_order), _dump_buf_dif);
+ free_pages((unsigned long)_dump_buf_dif, _dump_buf_dif_order);
+ }
}
module_init(lpfc_init);
diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h
index 39fd2b843bec..a85b7c196bbc 100644
--- a/drivers/scsi/lpfc/lpfc_logmsg.h
+++ b/drivers/scsi/lpfc/lpfc_logmsg.h
@@ -27,6 +27,7 @@
#define LOG_FCP 0x40 /* FCP traffic history */
#define LOG_NODE 0x80 /* Node table events */
#define LOG_TEMP 0x100 /* Temperature sensor events */
+#define LOG_BG 0x200 /* BlockBuard events */
#define LOG_MISC 0x400 /* Miscellaneous events */
#define LOG_SLI 0x800 /* SLI events */
#define LOG_FCP_ERROR 0x1000 /* log errors, not underruns */
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 7465fe746fe9..34eeb086a667 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -77,6 +77,38 @@ lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint16_t offset)
}
/**
+ * lpfc_dump_mem: Prepare a mailbox command for retrieving wakeup params.
+ * @phba: pointer to lpfc hba data structure.
+ * @pmb: pointer to the driver internal queue element for mailbox command.
+ * This function create a dump memory mailbox command to dump wake up
+ * parameters.
+ */
+void
+lpfc_dump_wakeup_param(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+ MAILBOX_t *mb;
+ void *ctx;
+
+ mb = &pmb->mb;
+ /* Save context so that we can restore after memset */
+ ctx = pmb->context2;
+
+ /* Setup to dump VPD region */
+ memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
+ mb->mbxCommand = MBX_DUMP_MEMORY;
+ mb->mbxOwner = OWN_HOST;
+ mb->un.varDmp.cv = 1;
+ mb->un.varDmp.type = DMP_NV_PARAMS;
+ mb->un.varDmp.entry_index = 0;
+ mb->un.varDmp.region_id = WAKE_UP_PARMS_REGION_ID;
+ mb->un.varDmp.word_cnt = WAKE_UP_PARMS_WORD_SIZE;
+ mb->un.varDmp.co = 0;
+ mb->un.varDmp.resp_offset = 0;
+ pmb->context2 = ctx;
+ return;
+}
+
+/**
* lpfc_read_nv: Prepare a mailbox command for reading HBA's NVRAM param.
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
@@ -1061,9 +1093,14 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
mb->un.varCfgPort.pcbLow = putPaddrLow(pdma_addr);
mb->un.varCfgPort.pcbHigh = putPaddrHigh(pdma_addr);
+ /* Always Host Group Pointer is in SLIM */
+ mb->un.varCfgPort.hps = 1;
+
/* If HBA supports SLI=3 ask for it */
if (phba->sli_rev == 3 && phba->vpd.sli3Feat.cerbm) {
+ if (phba->cfg_enable_bg)
+ mb->un.varCfgPort.cbg = 1; /* configure BlockGuard */
mb->un.varCfgPort.cerbm = 1; /* Request HBQs */
mb->un.varCfgPort.ccrp = 1; /* Command Ring Polling */
mb->un.varCfgPort.cinb = 1; /* Interrupt Notification Block */
@@ -1163,16 +1200,11 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
sizeof(*phba->host_gp));
}
- /* Setup Port Group ring pointer */
- if (phba->sli3_options & LPFC_SLI3_INB_ENABLED) {
- pgp_offset = offsetof(struct lpfc_sli2_slim,
- mbx.us.s3_inb_pgp.port);
- phba->hbq_get = phba->mbox->us.s3_inb_pgp.hbq_get;
- } else if (phba->sli_rev == 3) {
+ /* Setup Port Group offset */
+ if (phba->sli_rev == 3)
pgp_offset = offsetof(struct lpfc_sli2_slim,
mbx.us.s3_pgp.port);
- phba->hbq_get = phba->mbox->us.s3_pgp.hbq_get;
- } else
+ else
pgp_offset = offsetof(struct lpfc_sli2_slim, mbx.us.s2.port);
pdma_addr = phba->slim2p.phys + pgp_offset;
phba->pcb->pgpAddrHigh = putPaddrHigh(pdma_addr);
@@ -1285,10 +1317,12 @@ lpfc_mbox_get(struct lpfc_hba * phba)
void
lpfc_mbox_cmpl_put(struct lpfc_hba * phba, LPFC_MBOXQ_t * mbq)
{
+ unsigned long iflag;
+
/* This function expects to be called from interrupt context */
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
list_add_tail(&mbq->list, &phba->sli.mboxq_cmpl);
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_nl.h b/drivers/scsi/lpfc/lpfc_nl.h
index 1accb5a9f4e6..27d1a88a98fe 100644
--- a/drivers/scsi/lpfc/lpfc_nl.h
+++ b/drivers/scsi/lpfc/lpfc_nl.h
@@ -22,18 +22,20 @@
#define FC_REG_LINK_EVENT 0x0001 /* link up / down events */
#define FC_REG_RSCN_EVENT 0x0002 /* RSCN events */
#define FC_REG_CT_EVENT 0x0004 /* CT request events */
-#define FC_REG_DUMP_EVENT 0x0008 /* Dump events */
-#define FC_REG_TEMPERATURE_EVENT 0x0010 /* temperature events */
-#define FC_REG_ELS_EVENT 0x0020 /* lpfc els events */
-#define FC_REG_FABRIC_EVENT 0x0040 /* lpfc fabric events */
-#define FC_REG_SCSI_EVENT 0x0080 /* lpfc scsi events */
-#define FC_REG_BOARD_EVENT 0x0100 /* lpfc board events */
-#define FC_REG_ADAPTER_EVENT 0x0200 /* lpfc adapter events */
+#define FC_REG_DUMP_EVENT 0x0010 /* Dump events */
+#define FC_REG_TEMPERATURE_EVENT 0x0020 /* temperature events */
+#define FC_REG_VPORTRSCN_EVENT 0x0040 /* Vport RSCN events */
+#define FC_REG_ELS_EVENT 0x0080 /* lpfc els events */
+#define FC_REG_FABRIC_EVENT 0x0100 /* lpfc fabric events */
+#define FC_REG_SCSI_EVENT 0x0200 /* lpfc scsi events */
+#define FC_REG_BOARD_EVENT 0x0400 /* lpfc board events */
+#define FC_REG_ADAPTER_EVENT 0x0800 /* lpfc adapter events */
#define FC_REG_EVENT_MASK (FC_REG_LINK_EVENT | \
FC_REG_RSCN_EVENT | \
FC_REG_CT_EVENT | \
FC_REG_DUMP_EVENT | \
FC_REG_TEMPERATURE_EVENT | \
+ FC_REG_VPORTRSCN_EVENT | \
FC_REG_ELS_EVENT | \
FC_REG_FABRIC_EVENT | \
FC_REG_SCSI_EVENT | \
@@ -52,6 +54,13 @@
* The payload sent via the fc transport is one-way driver->application.
*/
+/* RSCN event header */
+struct lpfc_rscn_event_header {
+ uint32_t event_type;
+ uint32_t payload_length; /* RSCN data length in bytes */
+ uint32_t rscn_payload[];
+};
+
/* els event header */
struct lpfc_els_event_header {
uint32_t event_type;
@@ -65,6 +74,7 @@ struct lpfc_els_event_header {
#define LPFC_EVENT_PRLO_RCV 0x02
#define LPFC_EVENT_ADISC_RCV 0x04
#define LPFC_EVENT_LSRJT_RCV 0x08
+#define LPFC_EVENT_LOGO_RCV 0x10
/* special els lsrjt event */
struct lpfc_lsrjt_event {
@@ -74,6 +84,11 @@ struct lpfc_lsrjt_event {
uint32_t explanation;
};
+/* special els logo event */
+struct lpfc_logo_event {
+ struct lpfc_els_event_header header;
+ uint8_t logo_wwpn[8];
+};
/* fabric event header */
struct lpfc_fabric_event_header {
@@ -125,6 +140,7 @@ struct lpfc_scsi_varqueuedepth_event {
/* special case scsi check condition event */
struct lpfc_scsi_check_condition_event {
struct lpfc_scsi_event_header scsi_event;
+ uint8_t opcode;
uint8_t sense_key;
uint8_t asc;
uint8_t ascq;
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 0c25d97acb42..8f548adae9cc 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -1929,10 +1929,10 @@ lpfc_device_recov_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (vport->fc_flag & FC_RSCN_DEFERRED)
return ndlp->nlp_state;
+ lpfc_cancel_retry_delay_tmo(vport, ndlp);
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
spin_unlock_irq(shost->host_lock);
- lpfc_cancel_retry_delay_tmo(vport, ndlp);
return ndlp->nlp_state;
}
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index bd1867411821..b103b6ed4970 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -18,13 +18,14 @@
* more details, a copy of which can be found in the file COPYING *
* included with this package. *
*******************************************************************/
-
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+#include <asm/unaligned.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_fc.h>
@@ -43,6 +44,73 @@
#define LPFC_RESET_WAIT 2
#define LPFC_ABORT_WAIT 2
+int _dump_buf_done;
+
+static char *dif_op_str[] = {
+ "SCSI_PROT_NORMAL",
+ "SCSI_PROT_READ_INSERT",
+ "SCSI_PROT_WRITE_STRIP",
+ "SCSI_PROT_READ_STRIP",
+ "SCSI_PROT_WRITE_INSERT",
+ "SCSI_PROT_READ_PASS",
+ "SCSI_PROT_WRITE_PASS",
+ "SCSI_PROT_READ_CONVERT",
+ "SCSI_PROT_WRITE_CONVERT"
+};
+
+static void
+lpfc_debug_save_data(struct scsi_cmnd *cmnd)
+{
+ void *src, *dst;
+ struct scatterlist *sgde = scsi_sglist(cmnd);
+
+ if (!_dump_buf_data) {
+ printk(KERN_ERR "BLKGRD ERROR %s _dump_buf_data is NULL\n",
+ __func__);
+ return;
+ }
+
+
+ if (!sgde) {
+ printk(KERN_ERR "BLKGRD ERROR: data scatterlist is null\n");
+ return;
+ }
+
+ dst = (void *) _dump_buf_data;
+ while (sgde) {
+ src = sg_virt(sgde);
+ memcpy(dst, src, sgde->length);
+ dst += sgde->length;
+ sgde = sg_next(sgde);
+ }
+}
+
+static void
+lpfc_debug_save_dif(struct scsi_cmnd *cmnd)
+{
+ void *src, *dst;
+ struct scatterlist *sgde = scsi_prot_sglist(cmnd);
+
+ if (!_dump_buf_dif) {
+ printk(KERN_ERR "BLKGRD ERROR %s _dump_buf_data is NULL\n",
+ __func__);
+ return;
+ }
+
+ if (!sgde) {
+ printk(KERN_ERR "BLKGRD ERROR: prot scatterlist is null\n");
+ return;
+ }
+
+ dst = _dump_buf_dif;
+ while (sgde) {
+ src = sg_virt(sgde);
+ memcpy(dst, src, sgde->length);
+ dst += sgde->length;
+ sgde = sg_next(sgde);
+ }
+}
+
/**
* lpfc_update_stats: Update statistical data for the command completion.
* @phba: Pointer to HBA object.
@@ -66,6 +134,8 @@ lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
if (cmd->result)
return;
+ latency = jiffies_to_msecs((long)jiffies - (long)lpfc_cmd->start_time);
+
spin_lock_irqsave(shost->host_lock, flags);
if (!vport->stat_data_enabled ||
vport->stat_data_blocked ||
@@ -74,13 +144,15 @@ lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
spin_unlock_irqrestore(shost->host_lock, flags);
return;
}
- latency = jiffies_to_msecs(jiffies - lpfc_cmd->start_time);
if (phba->bucket_type == LPFC_LINEAR_BUCKET) {
i = (latency + phba->bucket_step - 1 - phba->bucket_base)/
phba->bucket_step;
- if (i >= LPFC_MAX_BUCKET_COUNT)
- i = LPFC_MAX_BUCKET_COUNT;
+ /* check array subscript bounds */
+ if (i < 0)
+ i = 0;
+ else if (i >= LPFC_MAX_BUCKET_COUNT)
+ i = LPFC_MAX_BUCKET_COUNT - 1;
} else {
for (i = 0; i < LPFC_MAX_BUCKET_COUNT-1; i++)
if (latency <= (phba->bucket_base +
@@ -92,7 +164,6 @@ lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
spin_unlock_irqrestore(shost->host_lock, flags);
}
-
/**
* lpfc_send_sdev_queuedepth_change_event: Posts a queuedepth change
* event.
@@ -148,12 +219,19 @@ lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba,
return;
}
-/*
- * This function is called with no lock held when there is a resource
- * error in driver or in firmware.
- */
+/**
+ * lpfc_rampdown_queue_depth: Post RAMP_DOWN_QUEUE event to worker thread.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine is called when there is resource error in driver or firmware.
+ * This routine posts WORKER_RAMP_DOWN_QUEUE event for @phba. This routine
+ * posts at most 1 event each second. This routine wakes up worker thread of
+ * @phba to process WORKER_RAM_DOWN_EVENT event.
+ *
+ * This routine should be called with no lock held.
+ **/
void
-lpfc_adjust_queue_depth(struct lpfc_hba *phba)
+lpfc_rampdown_queue_depth(struct lpfc_hba *phba)
{
unsigned long flags;
uint32_t evt_posted;
@@ -182,10 +260,17 @@ lpfc_adjust_queue_depth(struct lpfc_hba *phba)
return;
}
-/*
- * This function is called with no lock held when there is a successful
- * SCSI command completion.
- */
+/**
+ * lpfc_rampup_queue_depth: Post RAMP_UP_QUEUE event for worker thread.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine post WORKER_RAMP_UP_QUEUE event for @phba vport. This routine
+ * post at most 1 event every 5 minute after last_ramp_up_time or
+ * last_rsrc_error_time. This routine wakes up worker thread of @phba
+ * to process WORKER_RAM_DOWN_EVENT event.
+ *
+ * This routine should be called with no lock held.
+ **/
static inline void
lpfc_rampup_queue_depth(struct lpfc_vport *vport,
struct scsi_device *sdev)
@@ -217,6 +302,14 @@ lpfc_rampup_queue_depth(struct lpfc_vport *vport,
return;
}
+/**
+ * lpfc_ramp_down_queue_handler: WORKER_RAMP_DOWN_QUEUE event handler.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine is called to process WORKER_RAMP_DOWN_QUEUE event for worker
+ * thread.This routine reduces queue depth for all scsi device on each vport
+ * associated with @phba.
+ **/
void
lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
{
@@ -267,6 +360,15 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
atomic_set(&phba->num_cmd_success, 0);
}
+/**
+ * lpfc_ramp_up_queue_handler: WORKER_RAMP_UP_QUEUE event handler.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine is called to process WORKER_RAMP_UP_QUEUE event for worker
+ * thread.This routine increases queue depth for all scsi device on each vport
+ * associated with @phba by 1. This routine also sets @phba num_rsrc_err and
+ * num_cmd_success to zero.
+ **/
void
lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
{
@@ -336,14 +438,21 @@ lpfc_scsi_dev_block(struct lpfc_hba *phba)
lpfc_destroy_vport_work_array(phba, vports);
}
-/*
+/**
+ * lpfc_new_scsi_buf: Scsi buffer allocator.
+ * @vport: The virtual port for which this call being executed.
+ *
* This routine allocates a scsi buffer, which contains all the necessary
* information needed to initiate a SCSI I/O. The non-DMAable buffer region
* contains information to build the IOCB. The DMAable region contains
- * memory for the FCP CMND, FCP RSP, and the inital BPL. In addition to
- * allocating memeory, the FCP CMND and FCP RSP BDEs are setup in the BPL
+ * memory for the FCP CMND, FCP RSP, and the initial BPL. In addition to
+ * allocating memory, the FCP CMND and FCP RSP BDEs are setup in the BPL
* and the BPL BDE is setup in the IOCB.
- */
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to lpfc_scsi_buf data structure - Success
+ **/
static struct lpfc_scsi_buf *
lpfc_new_scsi_buf(struct lpfc_vport *vport)
{
@@ -407,14 +516,14 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport)
bpl[0].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_cmd));
bpl[0].tus.f.bdeSize = sizeof(struct fcp_cmnd);
bpl[0].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl[0].tus.w = le32_to_cpu(bpl->tus.w);
+ bpl[0].tus.w = le32_to_cpu(bpl[0].tus.w);
/* Setup the physical region for the FCP RSP */
bpl[1].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_rsp));
bpl[1].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_rsp));
bpl[1].tus.f.bdeSize = sizeof(struct fcp_rsp);
bpl[1].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl[1].tus.w = le32_to_cpu(bpl->tus.w);
+ bpl[1].tus.w = le32_to_cpu(bpl[1].tus.w);
/*
* Since the IOCB for the FCP I/O is built into this lpfc_scsi_buf,
@@ -422,7 +531,8 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport)
*/
iocb = &psb->cur_iocbq.iocb;
iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
- if (phba->sli_rev == 3) {
+ if ((phba->sli_rev == 3) &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) {
/* fill in immediate fcp command BDE */
iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_IMMED;
iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd);
@@ -452,6 +562,17 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport)
return psb;
}
+/**
+ * lpfc_get_scsi_buf: Get a scsi buffer from lpfc_scsi_buf_list list of Hba.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list
+ * and returns to caller.
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to lpfc_scsi_buf - Success
+ **/
static struct lpfc_scsi_buf*
lpfc_get_scsi_buf(struct lpfc_hba * phba)
{
@@ -464,11 +585,20 @@ lpfc_get_scsi_buf(struct lpfc_hba * phba)
if (lpfc_cmd) {
lpfc_cmd->seg_cnt = 0;
lpfc_cmd->nonsg_phys = 0;
+ lpfc_cmd->prot_seg_cnt = 0;
}
spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
return lpfc_cmd;
}
+/**
+ * lpfc_release_scsi_buf: Return a scsi buffer back to hba lpfc_scsi_buf_list list.
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is being released.
+ *
+ * This routine releases @psb scsi buffer by adding it to tail of @phba
+ * lpfc_scsi_buf_list list.
+ **/
static void
lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
{
@@ -480,6 +610,20 @@ lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
}
+/**
+ * lpfc_scsi_prep_dma_buf: Routine to do DMA mapping for scsi buffer.
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This routine does the pci dma mapping for scatter-gather list of scsi cmnd
+ * field of @lpfc_cmd. This routine scans through sg elements and format the
+ * bdea. This routine also initializes all IOCB fields which are dependent on
+ * scsi command request buffer.
+ *
+ * Return codes:
+ * 1 - Error
+ * 0 - Success
+ **/
static int
lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
{
@@ -516,7 +660,7 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
lpfc_cmd->seg_cnt = nseg;
if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
printk(KERN_ERR "%s: Too many sg segments from "
- "dma_map_sg. Config %d, seg_cnt %d",
+ "dma_map_sg. Config %d, seg_cnt %d\n",
__func__, phba->cfg_sg_seg_cnt,
lpfc_cmd->seg_cnt);
scsi_dma_unmap(scsi_cmnd);
@@ -535,6 +679,7 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
scsi_for_each_sg(scsi_cmnd, sgel, nseg, num_bde) {
physaddr = sg_dma_address(sgel);
if (phba->sli_rev == 3 &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
nseg <= LPFC_EXT_DATA_BDE_COUNT) {
data_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
data_bde->tus.f.bdeSize = sg_dma_len(sgel);
@@ -560,7 +705,8 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
* explicitly reinitialized and for SLI-3 the extended bde count is
* explicitly reinitialized since all iocb memory resources are reused.
*/
- if (phba->sli_rev == 3) {
+ if (phba->sli_rev == 3 &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) {
if (num_bde > LPFC_EXT_DATA_BDE_COUNT) {
/*
* The extended IOCB format can only fit 3 BDE or a BPL.
@@ -587,7 +733,683 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
((num_bde + 2) * sizeof(struct ulp_bde64));
}
fcp_cmnd->fcpDl = cpu_to_be32(scsi_bufflen(scsi_cmnd));
+
+ /*
+ * Due to difference in data length between DIF/non-DIF paths,
+ * we need to set word 4 of IOCB here
+ */
+ iocb_cmd->un.fcpi.fcpi_parm = le32_to_cpu(scsi_bufflen(scsi_cmnd));
+ return 0;
+}
+
+/*
+ * Given a scsi cmnd, determine the BlockGuard profile to be used
+ * with the cmd
+ */
+static int
+lpfc_sc_to_sli_prof(struct scsi_cmnd *sc)
+{
+ uint8_t guard_type = scsi_host_get_guard(sc->device->host);
+ uint8_t ret_prof = LPFC_PROF_INVALID;
+
+ if (guard_type == SHOST_DIX_GUARD_IP) {
+ switch (scsi_get_prot_op(sc)) {
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ ret_prof = LPFC_PROF_AST2;
+ break;
+
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ ret_prof = LPFC_PROF_A1;
+ break;
+
+ case SCSI_PROT_READ_CONVERT:
+ case SCSI_PROT_WRITE_CONVERT:
+ ret_prof = LPFC_PROF_AST1;
+ break;
+
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ case SCSI_PROT_NORMAL:
+ default:
+ printk(KERN_ERR "Bad op/guard:%d/%d combination\n",
+ scsi_get_prot_op(sc), guard_type);
+ break;
+
+ }
+ } else if (guard_type == SHOST_DIX_GUARD_CRC) {
+ switch (scsi_get_prot_op(sc)) {
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ ret_prof = LPFC_PROF_A1;
+ break;
+
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ ret_prof = LPFC_PROF_C1;
+ break;
+
+ case SCSI_PROT_READ_CONVERT:
+ case SCSI_PROT_WRITE_CONVERT:
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ case SCSI_PROT_NORMAL:
+ default:
+ printk(KERN_ERR "Bad op/guard:%d/%d combination\n",
+ scsi_get_prot_op(sc), guard_type);
+ break;
+ }
+ } else {
+ /* unsupported format */
+ BUG();
+ }
+
+ return ret_prof;
+}
+
+struct scsi_dif_tuple {
+ __be16 guard_tag; /* Checksum */
+ __be16 app_tag; /* Opaque storage */
+ __be32 ref_tag; /* Target LBA or indirect LBA */
+};
+
+static inline unsigned
+lpfc_cmd_blksize(struct scsi_cmnd *sc)
+{
+ return sc->device->sector_size;
+}
+
+/**
+ * lpfc_get_cmd_dif_parms - Extract DIF parameters from SCSI command
+ * @sc: in: SCSI command
+ * @apptagmask out: app tag mask
+ * @apptagval out: app tag value
+ * @reftag out: ref tag (reference tag)
+ *
+ * Description:
+ * Extract DIF paramters from the command if possible. Otherwise,
+ * use default paratmers.
+ *
+ **/
+static inline void
+lpfc_get_cmd_dif_parms(struct scsi_cmnd *sc, uint16_t *apptagmask,
+ uint16_t *apptagval, uint32_t *reftag)
+{
+ struct scsi_dif_tuple *spt;
+ unsigned char op = scsi_get_prot_op(sc);
+ unsigned int protcnt = scsi_prot_sg_count(sc);
+ static int cnt;
+
+ if (protcnt && (op == SCSI_PROT_WRITE_STRIP ||
+ op == SCSI_PROT_WRITE_PASS ||
+ op == SCSI_PROT_WRITE_CONVERT)) {
+
+ cnt++;
+ spt = page_address(sg_page(scsi_prot_sglist(sc))) +
+ scsi_prot_sglist(sc)[0].offset;
+ *apptagmask = 0;
+ *apptagval = 0;
+ *reftag = cpu_to_be32(spt->ref_tag);
+
+ } else {
+ /* SBC defines ref tag to be lower 32bits of LBA */
+ *reftag = (uint32_t) (0xffffffff & scsi_get_lba(sc));
+ *apptagmask = 0;
+ *apptagval = 0;
+ }
+}
+
+/*
+ * This function sets up buffer list for protection groups of
+ * type LPFC_PG_TYPE_NO_DIF
+ *
+ * This is usually used when the HBA is instructed to generate
+ * DIFs and insert them into data stream (or strip DIF from
+ * incoming data stream)
+ *
+ * The buffer list consists of just one protection group described
+ * below:
+ * +-------------------------+
+ * start of prot group --> | PDE_1 |
+ * +-------------------------+
+ * | Data BDE |
+ * +-------------------------+
+ * |more Data BDE's ... (opt)|
+ * +-------------------------+
+ *
+ * @sc: pointer to scsi command we're working on
+ * @bpl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ *
+ * Note: Data s/g buffers have been dma mapped
+ */
+static int
+lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ struct ulp_bde64 *bpl, int datasegcnt)
+{
+ struct scatterlist *sgde = NULL; /* s/g data entry */
+ struct lpfc_pde *pde1 = NULL;
+ dma_addr_t physaddr;
+ int i = 0, num_bde = 0;
+ int datadir = sc->sc_data_direction;
+ int prof = LPFC_PROF_INVALID;
+ unsigned blksize;
+ uint32_t reftag;
+ uint16_t apptagmask, apptagval;
+
+ pde1 = (struct lpfc_pde *) bpl;
+ prof = lpfc_sc_to_sli_prof(sc);
+
+ if (prof == LPFC_PROF_INVALID)
+ goto out;
+
+ /* extract some info from the scsi command for PDE1*/
+ blksize = lpfc_cmd_blksize(sc);
+ lpfc_get_cmd_dif_parms(sc, &apptagmask, &apptagval, &reftag);
+
+ /* setup PDE1 with what we have */
+ lpfc_pde_set_bg_parms(pde1, LPFC_PDE1_DESCRIPTOR, prof, blksize,
+ BG_EC_STOP_ERR);
+ lpfc_pde_set_dif_parms(pde1, apptagmask, apptagval, reftag);
+
+ num_bde++;
+ bpl++;
+
+ /* assumption: caller has already run dma_map_sg on command data */
+ scsi_for_each_sg(sc, sgde, datasegcnt, i) {
+ physaddr = sg_dma_address(sgde);
+ bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr));
+ bpl->tus.f.bdeSize = sg_dma_len(sgde);
+ if (datadir == DMA_TO_DEVICE)
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ else
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ bpl++;
+ num_bde++;
+ }
+
+out:
+ return num_bde;
+}
+
+/*
+ * This function sets up buffer list for protection groups of
+ * type LPFC_PG_TYPE_DIF_BUF
+ *
+ * This is usually used when DIFs are in their own buffers,
+ * separate from the data. The HBA can then by instructed
+ * to place the DIFs in the outgoing stream. For read operations,
+ * The HBA could extract the DIFs and place it in DIF buffers.
+ *
+ * The buffer list for this type consists of one or more of the
+ * protection groups described below:
+ * +-------------------------+
+ * start of first prot group --> | PDE_1 |
+ * +-------------------------+
+ * | PDE_3 (Prot BDE) |
+ * +-------------------------+
+ * | Data BDE |
+ * +-------------------------+
+ * |more Data BDE's ... (opt)|
+ * +-------------------------+
+ * start of new prot group --> | PDE_1 |
+ * +-------------------------+
+ * | ... |
+ * +-------------------------+
+ *
+ * @sc: pointer to scsi command we're working on
+ * @bpl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ * @protcnt: number of segment of protection data that have been dma mapped
+ *
+ * Note: It is assumed that both data and protection s/g buffers have been
+ * mapped for DMA
+ */
+static int
+lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ struct ulp_bde64 *bpl, int datacnt, int protcnt)
+{
+ struct scatterlist *sgde = NULL; /* s/g data entry */
+ struct scatterlist *sgpe = NULL; /* s/g prot entry */
+ struct lpfc_pde *pde1 = NULL;
+ struct ulp_bde64 *prot_bde = NULL;
+ dma_addr_t dataphysaddr, protphysaddr;
+ unsigned short curr_data = 0, curr_prot = 0;
+ unsigned int split_offset, protgroup_len;
+ unsigned int protgrp_blks, protgrp_bytes;
+ unsigned int remainder, subtotal;
+ int prof = LPFC_PROF_INVALID;
+ int datadir = sc->sc_data_direction;
+ unsigned char pgdone = 0, alldone = 0;
+ unsigned blksize;
+ uint32_t reftag;
+ uint16_t apptagmask, apptagval;
+ int num_bde = 0;
+
+ sgpe = scsi_prot_sglist(sc);
+ sgde = scsi_sglist(sc);
+
+ if (!sgpe || !sgde) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9020 Invalid s/g entry: data=0x%p prot=0x%p\n",
+ sgpe, sgde);
+ return 0;
+ }
+
+ prof = lpfc_sc_to_sli_prof(sc);
+ if (prof == LPFC_PROF_INVALID)
+ goto out;
+
+ /* extract some info from the scsi command for PDE1*/
+ blksize = lpfc_cmd_blksize(sc);
+ lpfc_get_cmd_dif_parms(sc, &apptagmask, &apptagval, &reftag);
+
+ split_offset = 0;
+ do {
+ /* setup the first PDE_1 */
+ pde1 = (struct lpfc_pde *) bpl;
+
+ lpfc_pde_set_bg_parms(pde1, LPFC_PDE1_DESCRIPTOR, prof, blksize,
+ BG_EC_STOP_ERR);
+ lpfc_pde_set_dif_parms(pde1, apptagmask, apptagval, reftag);
+
+ num_bde++;
+ bpl++;
+
+ /* setup the first BDE that points to protection buffer */
+ prot_bde = (struct ulp_bde64 *) bpl;
+ protphysaddr = sg_dma_address(sgpe);
+ prot_bde->addrLow = le32_to_cpu(putPaddrLow(protphysaddr));
+ prot_bde->addrHigh = le32_to_cpu(putPaddrHigh(protphysaddr));
+ protgroup_len = sg_dma_len(sgpe);
+
+
+ /* must be integer multiple of the DIF block length */
+ BUG_ON(protgroup_len % 8);
+
+ protgrp_blks = protgroup_len / 8;
+ protgrp_bytes = protgrp_blks * blksize;
+
+ prot_bde->tus.f.bdeSize = protgroup_len;
+ if (datadir == DMA_TO_DEVICE)
+ prot_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ else
+ prot_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ prot_bde->tus.w = le32_to_cpu(bpl->tus.w);
+
+ curr_prot++;
+ num_bde++;
+
+ /* setup BDE's for data blocks associated with DIF data */
+ pgdone = 0;
+ subtotal = 0; /* total bytes processed for current prot grp */
+ while (!pgdone) {
+ if (!sgde) {
+ printk(KERN_ERR "%s Invalid data segment\n",
+ __func__);
+ return 0;
+ }
+ bpl++;
+ dataphysaddr = sg_dma_address(sgde) + split_offset;
+ bpl->addrLow = le32_to_cpu(putPaddrLow(dataphysaddr));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(dataphysaddr));
+
+ remainder = sg_dma_len(sgde) - split_offset;
+
+ if ((subtotal + remainder) <= protgrp_bytes) {
+ /* we can use this whole buffer */
+ bpl->tus.f.bdeSize = remainder;
+ split_offset = 0;
+
+ if ((subtotal + remainder) == protgrp_bytes)
+ pgdone = 1;
+ } else {
+ /* must split this buffer with next prot grp */
+ bpl->tus.f.bdeSize = protgrp_bytes - subtotal;
+ split_offset += bpl->tus.f.bdeSize;
+ }
+
+ subtotal += bpl->tus.f.bdeSize;
+
+ if (datadir == DMA_TO_DEVICE)
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ else
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+ num_bde++;
+ curr_data++;
+
+ if (split_offset)
+ break;
+
+ /* Move to the next s/g segment if possible */
+ sgde = sg_next(sgde);
+ }
+
+ /* are we done ? */
+ if (curr_prot == protcnt) {
+ alldone = 1;
+ } else if (curr_prot < protcnt) {
+ /* advance to next prot buffer */
+ sgpe = sg_next(sgpe);
+ bpl++;
+
+ /* update the reference tag */
+ reftag += protgrp_blks;
+ } else {
+ /* if we're here, we have a bug */
+ printk(KERN_ERR "BLKGRD: bug in %s\n", __func__);
+ }
+
+ } while (!alldone);
+
+out:
+
+
+ return num_bde;
+}
+/*
+ * Given a SCSI command that supports DIF, determine composition of protection
+ * groups involved in setting up buffer lists
+ *
+ * Returns:
+ * for DIF (for both read and write)
+ * */
+static int
+lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc)
+{
+ int ret = LPFC_PG_TYPE_INVALID;
+ unsigned char op = scsi_get_prot_op(sc);
+
+ switch (op) {
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ ret = LPFC_PG_TYPE_NO_DIF;
+ break;
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ case SCSI_PROT_WRITE_CONVERT:
+ case SCSI_PROT_READ_CONVERT:
+ ret = LPFC_PG_TYPE_DIF_BUF;
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9021 Unsupported protection op:%d\n", op);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * This is the protection/DIF aware version of
+ * lpfc_scsi_prep_dma_buf(). It may be a good idea to combine the
+ * two functions eventually, but for now, it's here
+ */
+static int
+lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
+ struct lpfc_scsi_buf *lpfc_cmd)
+{
+ struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+ struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+ struct ulp_bde64 *bpl = lpfc_cmd->fcp_bpl;
+ IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+ uint32_t num_bde = 0;
+ int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction;
+ int prot_group_type = 0;
+ int diflen, fcpdl;
+ unsigned blksize;
+
+ /*
+ * Start the lpfc command prep by bumping the bpl beyond fcp_cmnd
+ * fcp_rsp regions to the first data bde entry
+ */
+ bpl += 2;
+ if (scsi_sg_count(scsi_cmnd)) {
+ /*
+ * The driver stores the segment count returned from pci_map_sg
+ * because this a count of dma-mappings used to map the use_sg
+ * pages. They are not guaranteed to be the same for those
+ * architectures that implement an IOMMU.
+ */
+ datasegcnt = dma_map_sg(&phba->pcidev->dev,
+ scsi_sglist(scsi_cmnd),
+ scsi_sg_count(scsi_cmnd), datadir);
+ if (unlikely(!datasegcnt))
+ return 1;
+
+ lpfc_cmd->seg_cnt = datasegcnt;
+ if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
+ printk(KERN_ERR "%s: Too many sg segments from "
+ "dma_map_sg. Config %d, seg_cnt %d\n",
+ __func__, phba->cfg_sg_seg_cnt,
+ lpfc_cmd->seg_cnt);
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ prot_group_type = lpfc_prot_group_type(phba, scsi_cmnd);
+
+ switch (prot_group_type) {
+ case LPFC_PG_TYPE_NO_DIF:
+ num_bde = lpfc_bg_setup_bpl(phba, scsi_cmnd, bpl,
+ datasegcnt);
+ /* we shoud have 2 or more entries in buffer list */
+ if (num_bde < 2)
+ goto err;
+ break;
+ case LPFC_PG_TYPE_DIF_BUF:{
+ /*
+ * This type indicates that protection buffers are
+ * passed to the driver, so that needs to be prepared
+ * for DMA
+ */
+ protsegcnt = dma_map_sg(&phba->pcidev->dev,
+ scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd), datadir);
+ if (unlikely(!protsegcnt)) {
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ lpfc_cmd->prot_seg_cnt = protsegcnt;
+ if (lpfc_cmd->prot_seg_cnt
+ > phba->cfg_prot_sg_seg_cnt) {
+ printk(KERN_ERR "%s: Too many prot sg segments "
+ "from dma_map_sg. Config %d,"
+ "prot_seg_cnt %d\n", __func__,
+ phba->cfg_prot_sg_seg_cnt,
+ lpfc_cmd->prot_seg_cnt);
+ dma_unmap_sg(&phba->pcidev->dev,
+ scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd),
+ datadir);
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ num_bde = lpfc_bg_setup_bpl_prot(phba, scsi_cmnd, bpl,
+ datasegcnt, protsegcnt);
+ /* we shoud have 3 or more entries in buffer list */
+ if (num_bde < 3)
+ goto err;
+ break;
+ }
+ case LPFC_PG_TYPE_INVALID:
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9022 Unexpected protection group %i\n",
+ prot_group_type);
+ return 1;
+ }
+ }
+
+ /*
+ * Finish initializing those IOCB fields that are dependent on the
+ * scsi_cmnd request_buffer. Note that the bdeSize is explicitly
+ * reinitialized since all iocb memory resources are used many times
+ * for transmit, receive, and continuation bpl's.
+ */
+ iocb_cmd->un.fcpi64.bdl.bdeSize = (2 * sizeof(struct ulp_bde64));
+ iocb_cmd->un.fcpi64.bdl.bdeSize += (num_bde * sizeof(struct ulp_bde64));
+ iocb_cmd->ulpBdeCount = 1;
+ iocb_cmd->ulpLe = 1;
+
+ fcpdl = scsi_bufflen(scsi_cmnd);
+
+ if (scsi_get_prot_type(scsi_cmnd) == SCSI_PROT_DIF_TYPE1) {
+ /*
+ * We are in DIF Type 1 mode
+ * Every data block has a 8 byte DIF (trailer)
+ * attached to it. Must ajust FCP data length
+ */
+ blksize = lpfc_cmd_blksize(scsi_cmnd);
+ diflen = (fcpdl / blksize) * 8;
+ fcpdl += diflen;
+ }
+ fcp_cmnd->fcpDl = be32_to_cpu(fcpdl);
+
+ /*
+ * Due to difference in data length between DIF/non-DIF paths,
+ * we need to set word 4 of IOCB here
+ */
+ iocb_cmd->un.fcpi.fcpi_parm = fcpdl;
+
return 0;
+err:
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9023 Could not setup all needed BDE's"
+ "prot_group_type=%d, num_bde=%d\n",
+ prot_group_type, num_bde);
+ return 1;
+}
+
+/*
+ * This function checks for BlockGuard errors detected by
+ * the HBA. In case of errors, the ASC/ASCQ fields in the
+ * sense buffer will be set accordingly, paired with
+ * ILLEGAL_REQUEST to signal to the kernel that the HBA
+ * detected corruption.
+ *
+ * Returns:
+ * 0 - No error found
+ * 1 - BlockGuard error found
+ * -1 - Internal error (bad profile, ...etc)
+ */
+static int
+lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
+ struct lpfc_iocbq *pIocbOut)
+{
+ struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
+ struct sli3_bg_fields *bgf = &pIocbOut->iocb.unsli3.sli3_bg;
+ int ret = 0;
+ uint32_t bghm = bgf->bghm;
+ uint32_t bgstat = bgf->bgstat;
+ uint64_t failing_sector = 0;
+
+ printk(KERN_ERR "BG ERROR in cmd 0x%x lba 0x%llx blk cnt 0x%lx "
+ "bgstat=0x%x bghm=0x%x\n",
+ cmd->cmnd[0], (unsigned long long)scsi_get_lba(cmd),
+ cmd->request->nr_sectors, bgstat, bghm);
+
+ spin_lock(&_dump_buf_lock);
+ if (!_dump_buf_done) {
+ printk(KERN_ERR "Saving Data for %u blocks to debugfs\n",
+ (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
+ lpfc_debug_save_data(cmd);
+
+ /* If we have a prot sgl, save the DIF buffer */
+ if (lpfc_prot_group_type(phba, cmd) ==
+ LPFC_PG_TYPE_DIF_BUF) {
+ printk(KERN_ERR "Saving DIF for %u blocks to debugfs\n",
+ (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
+ lpfc_debug_save_dif(cmd);
+ }
+
+ _dump_buf_done = 1;
+ }
+ spin_unlock(&_dump_buf_lock);
+
+ if (lpfc_bgs_get_invalid_prof(bgstat)) {
+ cmd->result = ScsiResult(DID_ERROR, 0);
+ printk(KERN_ERR "Invalid BlockGuard profile. bgstat:0x%x\n",
+ bgstat);
+ ret = (-1);
+ goto out;
+ }
+
+ if (lpfc_bgs_get_uninit_dif_block(bgstat)) {
+ cmd->result = ScsiResult(DID_ERROR, 0);
+ printk(KERN_ERR "Invalid BlockGuard DIF Block. bgstat:0x%x\n",
+ bgstat);
+ ret = (-1);
+ goto out;
+ }
+
+ if (lpfc_bgs_get_guard_err(bgstat)) {
+ ret = 1;
+
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x1);
+ cmd->result = (DRIVER_SENSE|SUGGEST_DIE) << 24
+ | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+ phba->bg_guard_err_cnt++;
+ printk(KERN_ERR "BLKGRD: guard_tag error\n");
+ }
+
+ if (lpfc_bgs_get_reftag_err(bgstat)) {
+ ret = 1;
+
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x3);
+ cmd->result = (DRIVER_SENSE|SUGGEST_DIE) << 24
+ | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+
+ phba->bg_reftag_err_cnt++;
+ printk(KERN_ERR "BLKGRD: ref_tag error\n");
+ }
+
+ if (lpfc_bgs_get_apptag_err(bgstat)) {
+ ret = 1;
+
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x2);
+ cmd->result = (DRIVER_SENSE|SUGGEST_DIE) << 24
+ | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+
+ phba->bg_apptag_err_cnt++;
+ printk(KERN_ERR "BLKGRD: app_tag error\n");
+ }
+
+ if (lpfc_bgs_get_hi_water_mark_present(bgstat)) {
+ /*
+ * setup sense data descriptor 0 per SPC-4 as an information
+ * field, and put the failing LBA in it
+ */
+ cmd->sense_buffer[8] = 0; /* Information */
+ cmd->sense_buffer[9] = 0xa; /* Add. length */
+ do_div(bghm, cmd->device->sector_size);
+
+ failing_sector = scsi_get_lba(cmd);
+ failing_sector += bghm;
+
+ put_unaligned_be64(failing_sector, &cmd->sense_buffer[10]);
+ }
+
+ if (!ret) {
+ /* No error was reported - problem in FW? */
+ cmd->result = ScsiResult(DID_ERROR, 0);
+ printk(KERN_ERR "BLKGRD: no errors reported!\n");
+ }
+
+out:
+ return ret;
}
/**
@@ -681,6 +1503,15 @@ lpfc_send_scsi_error_event(struct lpfc_hba *phba, struct lpfc_vport *vport,
lpfc_worker_wake_up(phba);
return;
}
+
+/**
+ * lpfc_scsi_unprep_dma_buf: Routine to un-map DMA mapping of scatter gather.
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is going to be un-mapped.
+ *
+ * This routine does DMA un-mapping of scatter gather list of scsi command
+ * field of @lpfc_cmd.
+ **/
static void
lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb)
{
@@ -692,8 +1523,22 @@ lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb)
*/
if (psb->seg_cnt > 0)
scsi_dma_unmap(psb->pCmd);
+ if (psb->prot_seg_cnt > 0)
+ dma_unmap_sg(&phba->pcidev->dev, scsi_prot_sglist(psb->pCmd),
+ scsi_prot_sg_count(psb->pCmd),
+ psb->pCmd->sc_data_direction);
}
+/**
+ * lpfc_handler_fcp_err: FCP response handler.
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @rsp_iocb: The response IOCB which contains FCP error.
+ *
+ * This routine is called to process response IOCB with status field
+ * IOSTAT_FCP_RSP_ERROR. This routine sets result field of scsi command
+ * based upon SCSI and FCP error.
+ **/
static void
lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
struct lpfc_iocbq *rsp_iocb)
@@ -735,7 +1580,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
logit = LOG_FCP;
lpfc_printf_vlog(vport, KERN_WARNING, logit,
- "0730 FCP command x%x failed: x%x SNS x%x x%x "
+ "9024 FCP command x%x failed: x%x SNS x%x x%x "
"Data: x%x x%x x%x x%x x%x\n",
cmnd->cmnd[0], scsi_status,
be32_to_cpu(*lp), be32_to_cpu(*(lp + 3)), resp_info,
@@ -758,7 +1603,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
scsi_set_resid(cmnd, be32_to_cpu(fcprsp->rspResId));
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "0716 FCP Read Underrun, expected %d, "
+ "9025 FCP Read Underrun, expected %d, "
"residual %d Data: x%x x%x x%x\n",
be32_to_cpu(fcpcmd->fcpDl),
scsi_get_resid(cmnd), fcpi_parm, cmnd->cmnd[0],
@@ -774,7 +1619,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
(scsi_get_resid(cmnd) != fcpi_parm)) {
lpfc_printf_vlog(vport, KERN_WARNING,
LOG_FCP | LOG_FCP_ERROR,
- "0735 FCP Read Check Error "
+ "9026 FCP Read Check Error "
"and Underrun Data: x%x x%x x%x x%x\n",
be32_to_cpu(fcpcmd->fcpDl),
scsi_get_resid(cmnd), fcpi_parm,
@@ -793,7 +1638,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
(scsi_bufflen(cmnd) - scsi_get_resid(cmnd)
< cmnd->underflow)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "0717 FCP command x%x residual "
+ "9027 FCP command x%x residual "
"underrun converted to error "
"Data: x%x x%x x%x\n",
cmnd->cmnd[0], scsi_bufflen(cmnd),
@@ -802,7 +1647,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
}
} else if (resp_info & RESID_OVER) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
- "0720 FCP command x%x residual overrun error. "
+ "9028 FCP command x%x residual overrun error. "
"Data: x%x x%x \n", cmnd->cmnd[0],
scsi_bufflen(cmnd), scsi_get_resid(cmnd));
host_status = DID_ERROR;
@@ -814,7 +1659,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
} else if ((scsi_status == SAM_STAT_GOOD) && fcpi_parm &&
(cmnd->sc_data_direction == DMA_FROM_DEVICE)) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP | LOG_FCP_ERROR,
- "0734 FCP Read Check Error Data: "
+ "9029 FCP Read Check Error Data: "
"x%x x%x x%x x%x\n",
be32_to_cpu(fcpcmd->fcpDl),
be32_to_cpu(fcprsp->rspResId),
@@ -828,6 +1673,16 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
lpfc_send_scsi_error_event(vport->phba, vport, lpfc_cmd, rsp_iocb);
}
+/**
+ * lpfc_scsi_cmd_iocb_cmpl: Scsi cmnd IOCB completion routine.
+ * @phba: The Hba for which this call is being executed.
+ * @pIocbIn: The command IOCBQ for the scsi cmnd.
+ * @pIocbOut: The response IOCBQ for the scsi cmnd .
+ *
+ * This routine assigns scsi command result by looking into response IOCB
+ * status field appropriately. This routine handles QUEUE FULL condition as
+ * well by ramping down device queue depth.
+ **/
static void
lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
struct lpfc_iocbq *pIocbOut)
@@ -846,7 +1701,8 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4];
lpfc_cmd->status = pIocbOut->iocb.ulpStatus;
- atomic_dec(&pnode->cmd_pending);
+ if (pnode && NLP_CHK_NODE_ACT(pnode))
+ atomic_dec(&pnode->cmd_pending);
if (lpfc_cmd->status) {
if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT &&
@@ -856,7 +1712,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_cmd->status = IOSTAT_DEFAULT;
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
- "0729 FCP cmd x%x failed <%d/%d> "
+ "9030 FCP cmd x%x failed <%d/%d> "
"status: x%x result: x%x Data: x%x x%x\n",
cmd->cmnd[0],
cmd->device ? cmd->device->id : 0xffff,
@@ -904,7 +1760,28 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_cmd->result == IOERR_ABORT_REQUESTED) {
cmd->result = ScsiResult(DID_REQUEUE, 0);
break;
- } /* else: fall through */
+ }
+
+ if ((lpfc_cmd->result == IOERR_RX_DMA_FAILED ||
+ lpfc_cmd->result == IOERR_TX_DMA_FAILED) &&
+ pIocbOut->iocb.unsli3.sli3_bg.bgstat) {
+ if (scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) {
+ /*
+ * This is a response for a BG enabled
+ * cmd. Parse BG error
+ */
+ lpfc_parse_bg_err(phba, lpfc_cmd,
+ pIocbOut);
+ break;
+ } else {
+ lpfc_printf_vlog(vport, KERN_WARNING,
+ LOG_BG,
+ "9031 non-zero BGSTAT "
+ "on unprotected cmd");
+ }
+ }
+
+ /* else: fall through */
default:
cmd->result = ScsiResult(DID_ERROR, 0);
break;
@@ -936,23 +1813,31 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
time_after(jiffies, lpfc_cmd->start_time +
msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) {
spin_lock_irqsave(sdev->host->host_lock, flags);
- if ((pnode->cmd_qdepth > atomic_read(&pnode->cmd_pending) &&
- (atomic_read(&pnode->cmd_pending) > LPFC_MIN_TGT_QDEPTH) &&
- ((cmd->cmnd[0] == READ_10) || (cmd->cmnd[0] == WRITE_10))))
- pnode->cmd_qdepth = atomic_read(&pnode->cmd_pending);
-
- pnode->last_change_time = jiffies;
+ if (pnode && NLP_CHK_NODE_ACT(pnode)) {
+ if (pnode->cmd_qdepth >
+ atomic_read(&pnode->cmd_pending) &&
+ (atomic_read(&pnode->cmd_pending) >
+ LPFC_MIN_TGT_QDEPTH) &&
+ ((cmd->cmnd[0] == READ_10) ||
+ (cmd->cmnd[0] == WRITE_10)))
+ pnode->cmd_qdepth =
+ atomic_read(&pnode->cmd_pending);
+
+ pnode->last_change_time = jiffies;
+ }
spin_unlock_irqrestore(sdev->host->host_lock, flags);
- } else if ((pnode->cmd_qdepth < LPFC_MAX_TGT_QDEPTH) &&
+ } else if (pnode && NLP_CHK_NODE_ACT(pnode)) {
+ if ((pnode->cmd_qdepth < LPFC_MAX_TGT_QDEPTH) &&
time_after(jiffies, pnode->last_change_time +
- msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) {
- spin_lock_irqsave(sdev->host->host_lock, flags);
- pnode->cmd_qdepth += pnode->cmd_qdepth *
- LPFC_TGTQ_RAMPUP_PCENT / 100;
- if (pnode->cmd_qdepth > LPFC_MAX_TGT_QDEPTH)
- pnode->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
- pnode->last_change_time = jiffies;
- spin_unlock_irqrestore(sdev->host->host_lock, flags);
+ msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) {
+ spin_lock_irqsave(sdev->host->host_lock, flags);
+ pnode->cmd_qdepth += pnode->cmd_qdepth *
+ LPFC_TGTQ_RAMPUP_PCENT / 100;
+ if (pnode->cmd_qdepth > LPFC_MAX_TGT_QDEPTH)
+ pnode->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
+ pnode->last_change_time = jiffies;
+ spin_unlock_irqrestore(sdev->host->host_lock, flags);
+ }
}
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
@@ -1067,6 +1952,15 @@ lpfc_fcpcmd_to_iocb(uint8_t *data, struct fcp_cmnd *fcp_cmnd)
}
}
+/**
+ * lpfc_scsi_prep_cmnd: Routine to convert scsi cmnd to FCP information unit.
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: The scsi command which needs to send.
+ * @pnode: Pointer to lpfc_nodelist.
+ *
+ * This routine initializes fcp_cmnd and iocb data structure from scsi command
+ * to transfer.
+ **/
static void
lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
struct lpfc_nodelist *pnode)
@@ -1122,7 +2016,6 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
} else {
iocb_cmd->ulpCommand = CMD_FCP_IREAD64_CR;
iocb_cmd->ulpPU = PARM_READ_CHECK;
- iocb_cmd->un.fcpi.fcpi_parm = scsi_bufflen(scsi_cmnd);
fcp_cmnd->fcpCntl3 = READ_DATA;
phba->fc4InputRequests++;
}
@@ -1133,7 +2026,8 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
fcp_cmnd->fcpCntl3 = 0;
phba->fc4ControlRequests++;
}
- if (phba->sli_rev == 3)
+ if (phba->sli_rev == 3 &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED))
lpfc_fcpcmd_to_iocb(iocb_cmd->unsli3.fcp_ext.icd, fcp_cmnd);
/*
* Finish initializing those IOCB fields that are independent
@@ -1152,6 +2046,19 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
piocbq->vport = vport;
}
+/**
+ * lpfc_scsi_prep_task_mgmt_cmnd: Convert scsi TM cmnd to FCP information unit.
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @lun: Logical unit number.
+ * @task_mgmt_cmd: SCSI task management command.
+ *
+ * This routine creates FCP information unit corresponding to @task_mgmt_cmd.
+ *
+ * Return codes:
+ * 0 - Error
+ * 1 - Success
+ **/
static int
lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
struct lpfc_scsi_buf *lpfc_cmd,
@@ -1178,7 +2085,8 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
memset(fcp_cmnd, 0, sizeof(struct fcp_cmnd));
int_to_scsilun(lun, &fcp_cmnd->fcp_lun);
fcp_cmnd->fcpCntl2 = task_mgmt_cmd;
- if (vport->phba->sli_rev == 3)
+ if (vport->phba->sli_rev == 3 &&
+ !(vport->phba->sli3_options & LPFC_SLI3_BG_ENABLED))
lpfc_fcpcmd_to_iocb(piocb->unsli3.fcp_ext.icd, fcp_cmnd);
piocb->ulpCommand = CMD_FCP_ICMND64_CR;
piocb->ulpContext = ndlp->nlp_rpi;
@@ -1201,6 +2109,15 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
return 1;
}
+/**
+ * lpc_taskmgmt_def_cmpl: IOCB completion routine for task management command.
+ * @phba: The Hba for which this call is being executed.
+ * @cmdiocbq: Pointer to lpfc_iocbq data structure.
+ * @rspiocbq: Pointer to lpfc_iocbq data structure.
+ *
+ * This routine is IOCB completion routine for device reset and target reset
+ * routine. This routine release scsi buffer associated with lpfc_cmd.
+ **/
static void
lpfc_tskmgmt_def_cmpl(struct lpfc_hba *phba,
struct lpfc_iocbq *cmdiocbq,
@@ -1213,6 +2130,20 @@ lpfc_tskmgmt_def_cmpl(struct lpfc_hba *phba,
return;
}
+/**
+ * lpfc_scsi_tgt_reset: Target reset handler.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure
+ * @vport: The virtual port for which this call is being executed.
+ * @tgt_id: Target ID.
+ * @lun: Lun number.
+ * @rdata: Pointer to lpfc_rport_data.
+ *
+ * This routine issues a TARGET RESET iocb to reset a target with @tgt_id ID.
+ *
+ * Return Code:
+ * 0x2003 - Error
+ * 0x2002 - Success.
+ **/
static int
lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport,
unsigned tgt_id, unsigned int lun,
@@ -1266,6 +2197,15 @@ lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport,
return ret;
}
+/**
+ * lpfc_info: Info entry point of scsi_host_template data structure.
+ * @host: The scsi host for which this call is being executed.
+ *
+ * This routine provides module information about hba.
+ *
+ * Reutrn code:
+ * Pointer to char - Success.
+ **/
const char *
lpfc_info(struct Scsi_Host *host)
{
@@ -1295,6 +2235,13 @@ lpfc_info(struct Scsi_Host *host)
return lpfcinfobuf;
}
+/**
+ * lpfc_poll_rearm_time: Routine to modify fcp_poll timer of hba.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine modifies fcp_poll_timer field of @phba by cfg_poll_tmo.
+ * The default value of cfg_poll_tmo is 10 milliseconds.
+ **/
static __inline__ void lpfc_poll_rearm_timer(struct lpfc_hba * phba)
{
unsigned long poll_tmo_expires =
@@ -1305,11 +2252,25 @@ static __inline__ void lpfc_poll_rearm_timer(struct lpfc_hba * phba)
poll_tmo_expires);
}
+/**
+ * lpfc_poll_start_timer: Routine to start fcp_poll_timer of HBA.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine starts the fcp_poll_timer of @phba.
+ **/
void lpfc_poll_start_timer(struct lpfc_hba * phba)
{
lpfc_poll_rearm_timer(phba);
}
+/**
+ * lpfc_poll_timeout: Restart polling timer.
+ * @ptr: Map to lpfc_hba data structure pointer.
+ *
+ * This routine restarts fcp_poll timer, when FCP ring polling is enable
+ * and FCP Ring interrupt is disable.
+ **/
+
void lpfc_poll_timeout(unsigned long ptr)
{
struct lpfc_hba *phba = (struct lpfc_hba *) ptr;
@@ -1321,6 +2282,20 @@ void lpfc_poll_timeout(unsigned long ptr)
}
}
+/**
+ * lpfc_queuecommand: Queuecommand entry point of Scsi Host Templater data
+ * structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ * @done: Pointer to done routine.
+ *
+ * Driver registers this routine to scsi midlayer to submit a @cmd to process.
+ * This routine prepares an IOCB from scsi command and provides to firmware.
+ * The @done callback is invoked after driver finished processing the command.
+ *
+ * Return value :
+ * 0 - Success
+ * SCSI_MLQUEUE_HOST_BUSY - Block all devices served by this host temporarily.
+ **/
static int
lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
{
@@ -1340,6 +2315,17 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
goto out_fail_command;
}
+ if (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
+ scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
+
+ printk(KERN_ERR "BLKGRD ERROR: rcvd protected cmd:%02x op:%02x "
+ "str=%s without registering for BlockGuard - "
+ "Rejecting command\n",
+ cmnd->cmnd[0], scsi_get_prot_op(cmnd),
+ dif_op_str[scsi_get_prot_op(cmnd)]);
+ goto out_fail_command;
+ }
+
/*
* Catch race where our node has transitioned, but the
* transport is still transitioning.
@@ -1348,12 +2334,13 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
cmnd->result = ScsiResult(DID_TRANSPORT_DISRUPTED, 0);
goto out_fail_command;
}
- if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth)
+ if (vport->cfg_max_scsicmpl_time &&
+ (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth))
goto out_host_busy;
lpfc_cmd = lpfc_get_scsi_buf(phba);
if (lpfc_cmd == NULL) {
- lpfc_adjust_queue_depth(phba);
+ lpfc_rampdown_queue_depth(phba);
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
"0707 driver's buffer pool is empty, "
@@ -1361,7 +2348,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
goto out_host_busy;
}
- lpfc_cmd->start_time = jiffies;
/*
* Store the midlayer's command structure for the completion phase
* and complete the command initialization.
@@ -1373,7 +2359,65 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
cmnd->host_scribble = (unsigned char *)lpfc_cmd;
cmnd->scsi_done = done;
- err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
+ if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9033 BLKGRD: rcvd protected cmd:%02x op:%02x "
+ "str=%s\n",
+ cmnd->cmnd[0], scsi_get_prot_op(cmnd),
+ dif_op_str[scsi_get_prot_op(cmnd)]);
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9034 BLKGRD: CDB: %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x \n",
+ cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
+ cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
+ cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
+ cmnd->cmnd[9]);
+ if (cmnd->cmnd[0] == READ_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9035 BLKGRD: READ @ sector %llu, "
+ "count %lu\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ cmnd->request->nr_sectors);
+ else if (cmnd->cmnd[0] == WRITE_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9036 BLKGRD: WRITE @ sector %llu, "
+ "count %lu cmd=%p\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ cmnd->request->nr_sectors,
+ cmnd);
+
+ err = lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
+ } else {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9038 BLKGRD: rcvd unprotected cmd:%02x op:%02x"
+ " str=%s\n",
+ cmnd->cmnd[0], scsi_get_prot_op(cmnd),
+ dif_op_str[scsi_get_prot_op(cmnd)]);
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9039 BLKGRD: CDB: %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x \n",
+ cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
+ cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
+ cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
+ cmnd->cmnd[9]);
+ if (cmnd->cmnd[0] == READ_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9040 dbg: READ @ sector %llu, "
+ "count %lu\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ cmnd->request->nr_sectors);
+ else if (cmnd->cmnd[0] == WRITE_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9041 dbg: WRITE @ sector %llu, "
+ "count %lu cmd=%p\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ cmnd->request->nr_sectors, cmnd);
+ else
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9042 dbg: parser not implemented\n");
+ err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
+ }
+
if (err)
goto out_host_busy_free_buf;
@@ -1382,9 +2426,10 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
atomic_inc(&ndlp->cmd_pending);
err = lpfc_sli_issue_iocb(phba, &phba->sli.ring[psli->fcp_ring],
&lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB);
- if (err)
+ if (err) {
+ atomic_dec(&ndlp->cmd_pending);
goto out_host_busy_free_buf;
-
+ }
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
lpfc_sli_poll_fcp_ring(phba);
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
@@ -1394,7 +2439,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
return 0;
out_host_busy_free_buf:
- atomic_dec(&ndlp->cmd_pending);
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
lpfc_release_scsi_buf(phba, lpfc_cmd);
out_host_busy:
@@ -1405,6 +2449,12 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
return 0;
}
+/**
+ * lpfc_block_error_handler: Routine to block error handler.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ *
+ * This routine blocks execution till fc_rport state is not FC_PORSTAT_BLCOEKD.
+ **/
static void
lpfc_block_error_handler(struct scsi_cmnd *cmnd)
{
@@ -1421,6 +2471,17 @@ lpfc_block_error_handler(struct scsi_cmnd *cmnd)
return;
}
+/**
+ * lpfc_abort_handler: Eh_abort_handler entry point of Scsi Host Template data
+ *structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ *
+ * This routine aborts @cmnd pending in base driver.
+ *
+ * Return code :
+ * 0x2003 - Error
+ * 0x2002 - Success
+ **/
static int
lpfc_abort_handler(struct scsi_cmnd *cmnd)
{
@@ -1516,6 +2577,18 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
return ret;
}
+/**
+ * lpfc_device_reset_handler: eh_device_reset entry point of Scsi Host Template
+ *data structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ *
+ * This routine does a device reset by sending a TARGET_RESET task management
+ * command.
+ *
+ * Return code :
+ * 0x2003 - Error
+ * 0ex2002 - Success
+ **/
static int
lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
{
@@ -1560,7 +2633,7 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
fc_get_event_number(),
sizeof(scsi_event),
(char *)&scsi_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
if (!rdata || pnode->nlp_state != NLP_STE_MAPPED_NODE) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
@@ -1633,6 +2706,17 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
return ret;
}
+/**
+ * lpfc_bus_reset_handler: eh_bus_reset_handler entry point of Scsi Host
+ * Template data structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ *
+ * This routine does target reset to all target on @cmnd->device->host.
+ *
+ * Return Code:
+ * 0x2003 - Error
+ * 0x2002 - Success
+ **/
static int
lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
{
@@ -1657,7 +2741,7 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
fc_get_event_number(),
sizeof(scsi_event),
(char *)&scsi_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
lpfc_block_error_handler(cmnd);
/*
@@ -1723,6 +2807,20 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
return ret;
}
+/**
+ * lpfc_slave_alloc: slave_alloc entry point of Scsi Host Template data
+ * structure.
+ * @sdev: Pointer to scsi_device.
+ *
+ * This routine populates the cmds_per_lun count + 2 scsi_bufs into this host's
+ * globally available list of scsi buffers. This routine also makes sure scsi
+ * buffer is not allocated more than HBA limit conveyed to midlayer. This list
+ * of scsi buffer exists for the lifetime of the driver.
+ *
+ * Return codes:
+ * non-0 - Error
+ * 0 - Success
+ **/
static int
lpfc_slave_alloc(struct scsi_device *sdev)
{
@@ -1784,6 +2882,19 @@ lpfc_slave_alloc(struct scsi_device *sdev)
return 0;
}
+/**
+ * lpfc_slave_configure: slave_configure entry point of Scsi Host Templater data
+ * structure.
+ * @sdev: Pointer to scsi_device.
+ *
+ * This routine configures following items
+ * - Tag command queuing support for @sdev if supported.
+ * - Dev loss time out value of fc_rport.
+ * - Enable SLI polling for fcp ring if ENABLE_FCP_RING_POLLING flag is set.
+ *
+ * Return codes:
+ * 0 - Success
+ **/
static int
lpfc_slave_configure(struct scsi_device *sdev)
{
@@ -1813,6 +2924,12 @@ lpfc_slave_configure(struct scsi_device *sdev)
return 0;
}
+/**
+ * lpfc_slave_destroy: slave_destroy entry point of SHT data structure.
+ * @sdev: Pointer to scsi_device.
+ *
+ * This routine sets @sdev hostatdata filed to null.
+ **/
static void
lpfc_slave_destroy(struct scsi_device *sdev)
{
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
index 437f182e2322..c7c440d5fa29 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.h
+++ b/drivers/scsi/lpfc/lpfc_scsi.h
@@ -124,6 +124,8 @@ struct lpfc_scsi_buf {
uint32_t seg_cnt; /* Number of scatter-gather segments returned by
* dma_map_sg. The driver needs this for calls
* to dma_unmap_sg. */
+ uint32_t prot_seg_cnt; /* seg_cnt's counterpart for protection data */
+
dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */
/*
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 8ab5babdeebc..01dfdc8696f8 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -542,6 +542,7 @@ lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
*/
nextiocb->iocb.ulpIoTag = (nextiocb->iocb_cmpl) ? nextiocb->iotag : 0;
+
if (pring->ringno == LPFC_ELS_RING) {
lpfc_debugfs_slow_ring_trc(phba,
"IOCB cmd ring: wd4:x%08x wd6:x%08x wd7:x%08x",
@@ -1259,68 +1260,6 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_replace_hbqbuff: Replace the HBQ buffer with a new buffer.
- * @phba: Pointer to HBA context object.
- * @tag: Tag for the HBQ buffer.
- *
- * This function is called from unsolicited event handler code path to get the
- * HBQ buffer associated with an unsolicited iocb. This function is called with
- * no lock held. It returns the buffer associated with the given tag and posts
- * another buffer to the firmware. Note that the new buffer must be allocated
- * before taking the hbalock and that the hba lock must be held until it is
- * finished with the hbq entry swap.
- **/
-static struct lpfc_dmabuf *
-lpfc_sli_replace_hbqbuff(struct lpfc_hba *phba, uint32_t tag)
-{
- struct hbq_dmabuf *hbq_entry, *new_hbq_entry;
- uint32_t hbqno;
- void *virt; /* virtual address ptr */
- dma_addr_t phys; /* mapped address */
- unsigned long flags;
-
- hbqno = tag >> 16;
- new_hbq_entry = (phba->hbqs[hbqno].hbq_alloc_buffer)(phba);
- /* Check whether HBQ is still in use */
- spin_lock_irqsave(&phba->hbalock, flags);
- if (!phba->hbq_in_use) {
- if (new_hbq_entry)
- (phba->hbqs[hbqno].hbq_free_buffer)(phba,
- new_hbq_entry);
- spin_unlock_irqrestore(&phba->hbalock, flags);
- return NULL;
- }
-
- hbq_entry = lpfc_sli_hbqbuf_find(phba, tag);
- if (hbq_entry == NULL) {
- if (new_hbq_entry)
- (phba->hbqs[hbqno].hbq_free_buffer)(phba,
- new_hbq_entry);
- spin_unlock_irqrestore(&phba->hbalock, flags);
- return NULL;
- }
- list_del(&hbq_entry->dbuf.list);
-
- if (new_hbq_entry == NULL) {
- list_add_tail(&hbq_entry->dbuf.list, &phba->hbqbuf_in_list);
- spin_unlock_irqrestore(&phba->hbalock, flags);
- return &hbq_entry->dbuf;
- }
- new_hbq_entry->tag = -1;
- phys = new_hbq_entry->dbuf.phys;
- virt = new_hbq_entry->dbuf.virt;
- new_hbq_entry->dbuf.phys = hbq_entry->dbuf.phys;
- new_hbq_entry->dbuf.virt = hbq_entry->dbuf.virt;
- hbq_entry->dbuf.phys = phys;
- hbq_entry->dbuf.virt = virt;
- lpfc_sli_free_hbq(phba, hbq_entry);
- list_add_tail(&new_hbq_entry->dbuf.list, &phba->hbqbuf_in_list);
- spin_unlock_irqrestore(&phba->hbalock, flags);
-
- return &new_hbq_entry->dbuf;
-}
-
-/**
* lpfc_sli_get_buff: Get the buffer associated with the buffer tag.
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
@@ -1334,13 +1273,17 @@ lpfc_sli_replace_hbqbuff(struct lpfc_hba *phba, uint32_t tag)
**/
static struct lpfc_dmabuf *
lpfc_sli_get_buff(struct lpfc_hba *phba,
- struct lpfc_sli_ring *pring,
- uint32_t tag)
+ struct lpfc_sli_ring *pring,
+ uint32_t tag)
{
+ struct hbq_dmabuf *hbq_entry;
+
if (tag & QUE_BUFTAG_BIT)
return lpfc_sli_ring_taggedbuf_get(phba, pring, tag);
- else
- return lpfc_sli_replace_hbqbuff(phba, tag);
+ hbq_entry = lpfc_sli_hbqbuf_find(phba, tag);
+ if (!hbq_entry)
+ return NULL;
+ return &hbq_entry->dbuf;
}
@@ -1372,8 +1315,6 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
match = 0;
irsp = &(saveq->iocb);
- if (irsp->ulpStatus == IOSTAT_NEED_BUFFER)
- return 1;
if (irsp->ulpCommand == CMD_ASYNC_STATUS) {
if (pring->lpfc_sli_rcv_async_status)
pring->lpfc_sli_rcv_async_status(phba, pring, saveq);
@@ -1982,7 +1923,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
(irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
spin_unlock_irqrestore(&phba->hbalock, iflag);
- lpfc_adjust_queue_depth(phba);
+ lpfc_rampdown_queue_depth(phba);
spin_lock_irqsave(&phba->hbalock, iflag);
}
@@ -2225,7 +2166,7 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
(irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
spin_unlock_irqrestore(&phba->hbalock, iflag);
- lpfc_adjust_queue_depth(phba);
+ lpfc_rampdown_queue_depth(phba);
spin_lock_irqsave(&phba->hbalock, iflag);
}
@@ -2790,7 +2731,6 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
{
MAILBOX_t *mb;
struct lpfc_sli *psli;
- uint16_t skip_post;
volatile uint32_t word0;
void __iomem *to_slim;
@@ -2815,13 +2755,10 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
readl(to_slim); /* flush */
/* Only skip post after fc_ffinit is completed */
- if (phba->pport->port_state) {
- skip_post = 1;
+ if (phba->pport->port_state)
word0 = 1; /* This is really setting up word1 */
- } else {
- skip_post = 0;
+ else
word0 = 0; /* This is really setting up word1 */
- }
to_slim = phba->MBslimaddr + sizeof (uint32_t);
writel(*(uint32_t *) mb, to_slim);
readl(to_slim); /* flush */
@@ -2835,10 +2772,8 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
memset(&psli->lnk_stat_offsets, 0, sizeof(psli->lnk_stat_offsets));
psli->stats_start = get_seconds();
- if (skip_post)
- mdelay(100);
- else
- mdelay(2000);
+ /* Give the INITFF and Post time to settle. */
+ mdelay(100);
lpfc_hba_down_post(phba);
@@ -3084,7 +3019,6 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
spin_unlock_irq(&phba->hbalock);
phba->pport->port_state = LPFC_VPORT_UNKNOWN;
lpfc_sli_brdrestart(phba);
- msleep(2500);
rc = lpfc_sli_chipset_init(phba);
if (rc)
break;
@@ -3111,7 +3045,8 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->sli3_options &= ~(LPFC_SLI3_NPIV_ENABLED |
LPFC_SLI3_HBQ_ENABLED |
LPFC_SLI3_CRP_ENABLED |
- LPFC_SLI3_INB_ENABLED);
+ LPFC_SLI3_INB_ENABLED |
+ LPFC_SLI3_BG_ENABLED);
if (rc != MBX_SUCCESS) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0442 Adapter failed to init, mbxCmd x%x "
@@ -3144,17 +3079,29 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->sli3_options |= LPFC_SLI3_CRP_ENABLED;
if (pmb->mb.un.varCfgPort.ginb) {
phba->sli3_options |= LPFC_SLI3_INB_ENABLED;
+ phba->hbq_get = phba->mbox->us.s3_inb_pgp.hbq_get;
phba->port_gp = phba->mbox->us.s3_inb_pgp.port;
phba->inb_ha_copy = &phba->mbox->us.s3_inb_pgp.ha_copy;
phba->inb_counter = &phba->mbox->us.s3_inb_pgp.counter;
phba->inb_last_counter =
phba->mbox->us.s3_inb_pgp.counter;
} else {
+ phba->hbq_get = phba->mbox->us.s3_pgp.hbq_get;
phba->port_gp = phba->mbox->us.s3_pgp.port;
phba->inb_ha_copy = NULL;
phba->inb_counter = NULL;
}
+
+ if (phba->cfg_enable_bg) {
+ if (pmb->mb.un.varCfgPort.gbg)
+ phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
+ else
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0443 Adapter did not grant "
+ "BlockGuard\n");
+ }
} else {
+ phba->hbq_get = NULL;
phba->port_gp = phba->mbox->us.s2.port;
phba->inb_ha_copy = NULL;
phba->inb_counter = NULL;
@@ -3305,10 +3252,6 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring;
- if (!(phba->pport->work_port_events & WORKER_MBOX_TMO)) {
- return;
- }
-
/* Mbox cmd <mbxCommand> timeout */
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
"0310 Mailbox command x%x timeout Data: x%x x%x x%p\n",
@@ -4005,7 +3948,7 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba,
shost = lpfc_shost_from_vport(phba->pport);
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(temp_event_data), (char *) &temp_event_data,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
}
@@ -5184,6 +5127,10 @@ lpfc_sli_check_eratt(struct lpfc_hba *phba)
{
uint32_t ha_copy;
+ /* If PCI channel is offline, don't process it */
+ if (unlikely(pci_channel_offline(phba->pcidev)))
+ return 0;
+
/* If somebody is waiting to handle an eratt, don't process it
* here. The brdkill function will do this.
*/
@@ -5242,6 +5189,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
uint32_t ha_copy;
uint32_t work_ha_copy;
unsigned long status;
+ unsigned long iflag;
uint32_t control;
MAILBOX_t *mbox, *pmbox;
@@ -5274,7 +5222,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
if (unlikely(phba->link_state < LPFC_LINK_DOWN))
return IRQ_NONE;
/* Need to read HA REG for slow-path events */
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
ha_copy = readl(phba->HAregaddr);
/* If somebody is waiting to handle an eratt don't process it
* here. The brdkill function will do this.
@@ -5294,7 +5242,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
writel((ha_copy & (HA_MBATT | HA_R2_CLR_MSK)),
phba->HAregaddr);
readl(phba->HAregaddr); /* flush */
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
} else
ha_copy = phba->ha_copy;
@@ -5307,13 +5255,13 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
* Turn off Link Attention interrupts
* until CLEAR_LA done
*/
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
phba->sli.sli_flag &= ~LPFC_PROCESS_LA;
control = readl(phba->HCregaddr);
control &= ~HC_LAINT_ENA;
writel(control, phba->HCregaddr);
readl(phba->HCregaddr); /* flush */
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
}
else
work_ha_copy &= ~HA_LATT;
@@ -5328,7 +5276,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
(HA_RXMASK << (4*LPFC_ELS_RING)));
status >>= (4*LPFC_ELS_RING);
if (status & HA_RXMASK) {
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
control = readl(phba->HCregaddr);
lpfc_debugfs_slow_ring_trc(phba,
@@ -5357,10 +5305,10 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
(uint32_t)((unsigned long)
&phba->work_waitq));
}
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
}
}
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
if (work_ha_copy & HA_ERATT)
lpfc_sli_read_hs(phba);
if ((work_ha_copy & HA_MBATT) && (phba->sli.mbox_active)) {
@@ -5372,7 +5320,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
/* First check out the status word */
lpfc_sli_pcimem_bcopy(mbox, pmbox, sizeof(uint32_t));
if (pmbox->mbxOwner != OWN_HOST) {
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
/*
* Stray Mailbox Interrupt, mbxCommand <cmd>
* mbxStatus <status>
@@ -5389,7 +5337,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
work_ha_copy &= ~HA_MBATT;
} else {
phba->sli.mbox_active = NULL;
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
phba->last_completion_time = jiffies;
del_timer(&phba->sli.mbox_tmo);
if (pmb->mbox_cmpl) {
@@ -5438,14 +5386,18 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
goto send_current_mbox;
}
}
- spin_lock(&phba->pport->work_port_lock);
+ spin_lock_irqsave(
+ &phba->pport->work_port_lock,
+ iflag);
phba->pport->work_port_events &=
~WORKER_MBOX_TMO;
- spin_unlock(&phba->pport->work_port_lock);
+ spin_unlock_irqrestore(
+ &phba->pport->work_port_lock,
+ iflag);
lpfc_mbox_cmpl_put(phba, pmb);
}
} else
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
if ((work_ha_copy & HA_MBATT) &&
(phba->sli.mbox_active == NULL)) {
@@ -5461,9 +5413,9 @@ send_current_mbox:
"MBX_SUCCESS");
}
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
phba->work_ha |= work_ha_copy;
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
lpfc_worker_wake_up(phba);
}
return IRQ_HANDLED;
@@ -5495,6 +5447,7 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
struct lpfc_hba *phba;
uint32_t ha_copy;
unsigned long status;
+ unsigned long iflag;
/* Get the driver's phba structure from the dev_id and
* assume the HBA is not interrupting.
@@ -5520,11 +5473,11 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
/* Need to read HA REG for FCP ring and other ring events */
ha_copy = readl(phba->HAregaddr);
/* Clear up only attention source related to fast-path */
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
writel((ha_copy & (HA_R0_CLR_MSK | HA_R1_CLR_MSK)),
phba->HAregaddr);
readl(phba->HAregaddr); /* flush */
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
} else
ha_copy = phba->ha_copy;
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index cc43e9de22cc..7e32e95c5392 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.2.8"
+#define LPFC_DRIVER_VERSION "8.3.0"
#define LPFC_DRIVER_NAME "lpfc"
#define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp"
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index a7de1cc02b40..63b54c66756c 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -288,10 +288,8 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
int vpi;
int rc = VPORT_ERROR;
int status;
- int size;
- if ((phba->sli_rev < 3) ||
- !(phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)) {
+ if ((phba->sli_rev < 3) || !(phba->cfg_enable_npiv)) {
lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
"1808 Create VPORT failed: "
"NPIV is not enabled: SLImode:%d\n",
@@ -351,20 +349,6 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
memcpy(vport->fc_portname.u.wwn, vport->fc_sparam.portName.u.wwn, 8);
memcpy(vport->fc_nodename.u.wwn, vport->fc_sparam.nodeName.u.wwn, 8);
- size = strnlen(fc_vport->symbolic_name, LPFC_VNAME_LEN);
- if (size) {
- vport->vname = kzalloc(size+1, GFP_KERNEL);
- if (!vport->vname) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
- "1814 Create VPORT failed. "
- "vname allocation failed.\n");
- rc = VPORT_ERROR;
- lpfc_free_vpi(phba, vpi);
- destroy_port(vport);
- goto error_out;
- }
- memcpy(vport->vname, fc_vport->symbolic_name, size+1);
- }
if (fc_vport->node_name != 0)
u64_to_wwn(fc_vport->node_name, vport->fc_nodename.u.wwn);
if (fc_vport->port_name != 0)
@@ -394,6 +378,9 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
goto error_out;
}
+ /* Create binary sysfs attribute for vport */
+ lpfc_alloc_sysfs_attr(vport);
+
*(struct lpfc_vport **)fc_vport->dd_data = vport;
vport->fc_vport = fc_vport;
@@ -405,6 +392,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
}
if (disable) {
+ lpfc_vport_set_state(vport, FC_VPORT_DISABLED);
rc = VPORT_OK;
goto out;
}
@@ -587,8 +575,12 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
spin_lock_irq(&phba->hbalock);
vport->load_flag |= FC_UNLOADING;
spin_unlock_irq(&phba->hbalock);
- kfree(vport->vname);
+
+ lpfc_free_sysfs_attr(vport);
+
lpfc_debugfs_terminate(vport);
+
+ /* Remove FC host and then SCSI host with the vport */
fc_remove_host(lpfc_shost_from_vport(vport));
scsi_remove_host(lpfc_shost_from_vport(vport));
diff --git a/drivers/scsi/mac_esp.c b/drivers/scsi/mac_esp.c
index 887682a24e36..c24e86f07804 100644
--- a/drivers/scsi/mac_esp.c
+++ b/drivers/scsi/mac_esp.c
@@ -53,7 +53,8 @@ struct mac_esp_priv {
void __iomem *pdma_io;
int error;
};
-static struct platform_device *internal_esp, *external_esp;
+static struct platform_device *internal_pdev, *external_pdev;
+static struct esp *esp_chips[2];
#define MAC_ESP_GET_PRIV(esp) ((struct mac_esp_priv *) \
platform_get_drvdata((struct platform_device *) \
@@ -170,7 +171,7 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp)
#define MAC_ESP_PDMA_LOOP(operands) \
asm volatile ( \
- " tstw %2 \n" \
+ " tstw %1 \n" \
" jbeq 20f \n" \
"1: movew " operands " \n" \
"2: movew " operands " \n" \
@@ -188,14 +189,14 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp)
"14: movew " operands " \n" \
"15: movew " operands " \n" \
"16: movew " operands " \n" \
- " subqw #1,%2 \n" \
+ " subqw #1,%1 \n" \
" jbne 1b \n" \
- "20: tstw %3 \n" \
+ "20: tstw %2 \n" \
" jbeq 30f \n" \
"21: movew " operands " \n" \
- " subqw #1,%3 \n" \
+ " subqw #1,%2 \n" \
" jbne 21b \n" \
- "30: tstw %4 \n" \
+ "30: tstw %3 \n" \
" jbeq 40f \n" \
"31: moveb " operands " \n" \
"32: nop \n" \
@@ -223,8 +224,8 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp)
" .long 31b,40b \n" \
" .long 32b,40b \n" \
" .previous \n" \
- : "+a" (addr) \
- : "a" (mep->pdma_io), "r" (count32), "r" (count2), "g" (esp_count))
+ : "+a" (addr), "+r" (count32), "+r" (count2) \
+ : "g" (count1), "a" (mep->pdma_io))
static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
u32 dma_count, int write, u8 cmd)
@@ -247,19 +248,20 @@ static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
do {
unsigned int count32 = esp_count >> 5;
unsigned int count2 = (esp_count & 0x1F) >> 1;
+ unsigned int count1 = esp_count & 1;
unsigned int start_addr = addr;
if (mac_esp_wait_for_dreq(esp))
break;
if (write) {
- MAC_ESP_PDMA_LOOP("%1@,%0@+");
+ MAC_ESP_PDMA_LOOP("%4@,%0@+");
esp_count -= addr - start_addr;
} else {
unsigned int n;
- MAC_ESP_PDMA_LOOP("%0@+,%1@");
+ MAC_ESP_PDMA_LOOP("%0@+,%4@");
if (mac_esp_wait_for_empty_fifo(esp))
break;
@@ -442,6 +444,32 @@ static u32 mac_esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
return dma_len > 0xFFFF ? 0xFFFF : dma_len;
}
+static irqreturn_t mac_scsi_esp_intr(int irq, void *dev_id)
+{
+ int got_intr;
+
+ /*
+ * This is an edge triggered IRQ, so we have to be careful to
+ * avoid missing a transition when it is shared by two ESP devices.
+ */
+
+ do {
+ got_intr = 0;
+ if (esp_chips[0] &&
+ (mac_esp_read8(esp_chips[0], ESP_STATUS) & ESP_STAT_INTR)) {
+ (void)scsi_esp_intr(irq, esp_chips[0]);
+ got_intr = 1;
+ }
+ if (esp_chips[1] &&
+ (mac_esp_read8(esp_chips[1], ESP_STATUS) & ESP_STAT_INTR)) {
+ (void)scsi_esp_intr(irq, esp_chips[1]);
+ got_intr = 1;
+ }
+ } while (got_intr);
+
+ return IRQ_HANDLED;
+}
+
static struct esp_driver_ops mac_esp_ops = {
.esp_write8 = mac_esp_write8,
.esp_read8 = mac_esp_read8,
@@ -556,10 +584,16 @@ static int __devinit esp_mac_probe(struct platform_device *dev)
}
host->irq = IRQ_MAC_SCSI;
- err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "Mac ESP",
- esp);
- if (err < 0)
- goto fail_free_priv;
+ esp_chips[dev->id] = esp;
+ mb();
+ if (esp_chips[!dev->id] == NULL) {
+ err = request_irq(host->irq, mac_scsi_esp_intr, 0,
+ "Mac ESP", NULL);
+ if (err < 0) {
+ esp_chips[dev->id] = NULL;
+ goto fail_free_priv;
+ }
+ }
err = scsi_esp_register(esp, &dev->dev);
if (err)
@@ -568,7 +602,8 @@ static int __devinit esp_mac_probe(struct platform_device *dev)
return 0;
fail_free_irq:
- free_irq(host->irq, esp);
+ if (esp_chips[!dev->id] == NULL)
+ free_irq(host->irq, esp);
fail_free_priv:
kfree(mep);
fail_free_command_block:
@@ -587,7 +622,9 @@ static int __devexit esp_mac_remove(struct platform_device *dev)
scsi_esp_unregister(esp);
- free_irq(irq, esp);
+ esp_chips[dev->id] = NULL;
+ if (!(esp_chips[0] || esp_chips[1]))
+ free_irq(irq, NULL);
kfree(mep);
@@ -614,19 +651,18 @@ static int __init mac_esp_init(void)
if (err)
return err;
- internal_esp = platform_device_alloc(DRV_MODULE_NAME, 0);
- if (internal_esp && platform_device_add(internal_esp)) {
- platform_device_put(internal_esp);
- internal_esp = NULL;
+ internal_pdev = platform_device_alloc(DRV_MODULE_NAME, 0);
+ if (internal_pdev && platform_device_add(internal_pdev)) {
+ platform_device_put(internal_pdev);
+ internal_pdev = NULL;
}
-
- external_esp = platform_device_alloc(DRV_MODULE_NAME, 1);
- if (external_esp && platform_device_add(external_esp)) {
- platform_device_put(external_esp);
- external_esp = NULL;
+ external_pdev = platform_device_alloc(DRV_MODULE_NAME, 1);
+ if (external_pdev && platform_device_add(external_pdev)) {
+ platform_device_put(external_pdev);
+ external_pdev = NULL;
}
- if (internal_esp || external_esp) {
+ if (internal_pdev || external_pdev) {
return 0;
} else {
platform_driver_unregister(&esp_mac_driver);
@@ -638,13 +674,13 @@ static void __exit mac_esp_exit(void)
{
platform_driver_unregister(&esp_mac_driver);
- if (internal_esp) {
- platform_device_unregister(internal_esp);
- internal_esp = NULL;
+ if (internal_pdev) {
+ platform_device_unregister(internal_pdev);
+ internal_pdev = NULL;
}
- if (external_esp) {
- platform_device_unregister(external_esp);
- external_esp = NULL;
+ if (external_pdev) {
+ platform_device_unregister(external_pdev);
+ external_pdev = NULL;
}
}
diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c
index 22052bb7becb..d06ec5aa6924 100644
--- a/drivers/scsi/nsp32.c
+++ b/drivers/scsi/nsp32.c
@@ -3401,8 +3401,7 @@ static int __devinit nsp32_probe(struct pci_dev *pdev, const struct pci_device_i
data->IrqNumber = pdev->irq;
data->BaseAddress = pci_resource_start(pdev, 0);
data->NumAddress = pci_resource_len (pdev, 0);
- data->MmioAddress = ioremap_nocache(pci_resource_start(pdev, 1),
- pci_resource_len (pdev, 1));
+ data->MmioAddress = pci_ioremap_bar(pdev, 1);
data->MmioLength = pci_resource_len (pdev, 1);
pci_set_master(pdev);
diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
index b6cd12b2e996..8cb9240596ab 100644
--- a/drivers/scsi/qla1280.c
+++ b/drivers/scsi/qla1280.c
@@ -4294,8 +4294,7 @@ qla1280_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
error = -ENODEV;
#if MEMORY_MAPPED_IO
- ha->mmpbase = ioremap(pci_resource_start(ha->pdev, 1),
- pci_resource_len(ha->pdev, 1));
+ ha->mmpbase = pci_ioremap_bar(ha->pdev, 1);
if (!ha->mmpbase) {
printk(KERN_INFO "qla1280: Unable to map I/O memory\n");
goto error_free_response_ring;
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index ed731968f15f..cd53627cc761 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -19,8 +19,9 @@ qla2x00_sysfs_read_fw_dump(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
if (ha->fw_dump_reading == 0)
return 0;
@@ -34,8 +35,9 @@ qla2x00_sysfs_write_fw_dump(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
int reading;
if (off != 0)
@@ -48,7 +50,7 @@ qla2x00_sysfs_write_fw_dump(struct kobject *kobj,
break;
qla_printk(KERN_INFO, ha,
- "Firmware dump cleared on (%ld).\n", ha->host_no);
+ "Firmware dump cleared on (%ld).\n", vha->host_no);
ha->fw_dump_reading = 0;
ha->fw_dumped = 0;
@@ -59,14 +61,14 @@ qla2x00_sysfs_write_fw_dump(struct kobject *kobj,
qla_printk(KERN_INFO, ha,
"Raw firmware dump ready for read on (%ld).\n",
- ha->host_no);
+ vha->host_no);
}
break;
case 2:
- qla2x00_alloc_fw_dump(ha);
+ qla2x00_alloc_fw_dump(vha);
break;
case 3:
- qla2x00_system_error(ha);
+ qla2x00_system_error(vha);
break;
}
return (count);
@@ -87,8 +89,9 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
if (!capable(CAP_SYS_ADMIN))
return 0;
@@ -103,8 +106,9 @@ qla2x00_sysfs_write_nvram(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
uint16_t cnt;
if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->nvram_size)
@@ -134,11 +138,11 @@ qla2x00_sysfs_write_nvram(struct kobject *kobj,
}
/* Write NVRAM. */
- ha->isp_ops->write_nvram(ha, (uint8_t *)buf, ha->nvram_base, count);
- ha->isp_ops->read_nvram(ha, (uint8_t *)ha->nvram, ha->nvram_base,
+ ha->isp_ops->write_nvram(vha, (uint8_t *)buf, ha->nvram_base, count);
+ ha->isp_ops->read_nvram(vha, (uint8_t *)ha->nvram, ha->nvram_base,
count);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
return (count);
}
@@ -158,8 +162,9 @@ qla2x00_sysfs_read_optrom(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
if (ha->optrom_state != QLA_SREADING)
return 0;
@@ -173,8 +178,9 @@ qla2x00_sysfs_write_optrom(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
if (ha->optrom_state != QLA_SWRITING)
return -EINVAL;
@@ -203,8 +209,10 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+
uint32_t start = 0;
uint32_t size = ha->optrom_size;
int val, valid;
@@ -262,7 +270,7 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj,
ha->optrom_region_start, ha->optrom_region_size));
memset(ha->optrom_buffer, 0, ha->optrom_region_size);
- ha->isp_ops->read_optrom(ha, ha->optrom_buffer,
+ ha->isp_ops->read_optrom(vha, ha->optrom_buffer,
ha->optrom_region_start, ha->optrom_region_size);
break;
case 2:
@@ -333,7 +341,7 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj,
"Writing flash region -- 0x%x/0x%x.\n",
ha->optrom_region_start, ha->optrom_region_size));
- ha->isp_ops->write_optrom(ha, ha->optrom_buffer,
+ ha->isp_ops->write_optrom(vha, ha->optrom_buffer,
ha->optrom_region_start, ha->optrom_region_size);
break;
default:
@@ -356,8 +364,9 @@ qla2x00_sysfs_read_vpd(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
if (!capable(CAP_SYS_ADMIN))
return 0;
@@ -371,15 +380,16 @@ qla2x00_sysfs_write_vpd(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->vpd_size)
return 0;
/* Write NVRAM. */
- ha->isp_ops->write_nvram(ha, (uint8_t *)buf, ha->vpd_base, count);
- ha->isp_ops->read_nvram(ha, (uint8_t *)ha->vpd, ha->vpd_base, count);
+ ha->isp_ops->write_nvram(vha, (uint8_t *)buf, ha->vpd_base, count);
+ ha->isp_ops->read_nvram(vha, (uint8_t *)ha->vpd, ha->vpd_base, count);
return count;
}
@@ -399,8 +409,9 @@ qla2x00_sysfs_read_sfp(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
uint16_t iter, addr, offset;
int rval;
@@ -429,7 +440,7 @@ do_read:
offset = 0;
}
- rval = qla2x00_read_sfp(ha, ha->sfp_data_dma, addr, offset,
+ rval = qla2x00_read_sfp(vha, ha->sfp_data_dma, addr, offset,
SFP_BLOCK_SIZE);
if (rval != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
@@ -469,30 +480,31 @@ static struct sysfs_entry {
};
void
-qla2x00_alloc_sysfs_attr(scsi_qla_host_t *ha)
+qla2x00_alloc_sysfs_attr(scsi_qla_host_t *vha)
{
- struct Scsi_Host *host = ha->host;
+ struct Scsi_Host *host = vha->host;
struct sysfs_entry *iter;
int ret;
for (iter = bin_file_entries; iter->name; iter++) {
- if (iter->is4GBp_only && !IS_FWI2_CAPABLE(ha))
+ if (iter->is4GBp_only && !IS_FWI2_CAPABLE(vha->hw))
continue;
ret = sysfs_create_bin_file(&host->shost_gendev.kobj,
iter->attr);
if (ret)
- qla_printk(KERN_INFO, ha,
+ qla_printk(KERN_INFO, vha->hw,
"Unable to create sysfs %s binary attribute "
"(%d).\n", iter->name, ret);
}
}
void
-qla2x00_free_sysfs_attr(scsi_qla_host_t *ha)
+qla2x00_free_sysfs_attr(scsi_qla_host_t *vha)
{
- struct Scsi_Host *host = ha->host;
+ struct Scsi_Host *host = vha->host;
struct sysfs_entry *iter;
+ struct qla_hw_data *ha = vha->hw;
for (iter = bin_file_entries; iter->name; iter++) {
if (iter->is4GBp_only && !IS_FWI2_CAPABLE(ha))
@@ -503,7 +515,7 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *ha)
}
if (ha->beacon_blink_led == 1)
- ha->isp_ops->beacon_off(ha);
+ ha->isp_ops->beacon_off(vha);
}
/* Scsi_Host attributes. */
@@ -519,22 +531,24 @@ static ssize_t
qla2x00_fw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
- char fw_str[30];
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ char fw_str[128];
return snprintf(buf, PAGE_SIZE, "%s\n",
- ha->isp_ops->fw_version_str(ha, fw_str));
+ ha->isp_ops->fw_version_str(vha, fw_str));
}
static ssize_t
qla2x00_serial_num_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
uint32_t sn;
if (IS_FWI2_CAPABLE(ha)) {
- qla2xxx_get_vpd_field(ha, "SN", buf, PAGE_SIZE);
+ qla2xxx_get_vpd_field(vha, "SN", buf, PAGE_SIZE);
return snprintf(buf, PAGE_SIZE, "%s\n", buf);
}
@@ -547,15 +561,16 @@ static ssize_t
qla2x00_isp_name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
- return snprintf(buf, PAGE_SIZE, "ISP%04X\n", ha->pdev->device);
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ return snprintf(buf, PAGE_SIZE, "ISP%04X\n", vha->hw->pdev->device);
}
static ssize_t
qla2x00_isp_id_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
return snprintf(buf, PAGE_SIZE, "%04x %04x %04x %04x\n",
ha->product_id[0], ha->product_id[1], ha->product_id[2],
ha->product_id[3]);
@@ -565,43 +580,44 @@ static ssize_t
qla2x00_model_name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
- return snprintf(buf, PAGE_SIZE, "%s\n", ha->model_number);
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ return snprintf(buf, PAGE_SIZE, "%s\n", vha->hw->model_number);
}
static ssize_t
qla2x00_model_desc_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
return snprintf(buf, PAGE_SIZE, "%s\n",
- ha->model_desc ? ha->model_desc: "");
+ vha->hw->model_desc ? vha->hw->model_desc : "");
}
static ssize_t
qla2x00_pci_info_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
char pci_info[30];
return snprintf(buf, PAGE_SIZE, "%s\n",
- ha->isp_ops->pci_info_str(ha, pci_info));
+ vha->hw->isp_ops->pci_info_str(vha, pci_info));
}
static ssize_t
qla2x00_link_state_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
int len = 0;
- if (atomic_read(&ha->loop_state) == LOOP_DOWN ||
- atomic_read(&ha->loop_state) == LOOP_DEAD)
+ if (atomic_read(&vha->loop_state) == LOOP_DOWN ||
+ atomic_read(&vha->loop_state) == LOOP_DEAD)
len = snprintf(buf, PAGE_SIZE, "Link Down\n");
- else if (atomic_read(&ha->loop_state) != LOOP_READY ||
- test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) ||
- test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags))
+ else if (atomic_read(&vha->loop_state) != LOOP_READY ||
+ test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) ||
+ test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
len = snprintf(buf, PAGE_SIZE, "Unknown Link State\n");
else {
len = snprintf(buf, PAGE_SIZE, "Link Up - ");
@@ -632,10 +648,10 @@ static ssize_t
qla2x00_zio_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
int len = 0;
- switch (ha->zio_mode) {
+ switch (vha->hw->zio_mode) {
case QLA_ZIO_MODE_6:
len += snprintf(buf + len, PAGE_SIZE-len, "Mode 6\n");
break;
@@ -650,7 +666,8 @@ static ssize_t
qla2x00_zio_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
int val = 0;
uint16_t zio_mode;
@@ -668,7 +685,7 @@ qla2x00_zio_store(struct device *dev, struct device_attribute *attr,
/* Update per-hba values and queue a reset. */
if (zio_mode != QLA_ZIO_DISABLED || ha->zio_mode != QLA_ZIO_DISABLED) {
ha->zio_mode = zio_mode;
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
}
return strlen(buf);
}
@@ -677,16 +694,16 @@ static ssize_t
qla2x00_zio_timer_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
- return snprintf(buf, PAGE_SIZE, "%d us\n", ha->zio_timer * 100);
+ return snprintf(buf, PAGE_SIZE, "%d us\n", vha->hw->zio_timer * 100);
}
static ssize_t
qla2x00_zio_timer_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
int val = 0;
uint16_t zio_timer;
@@ -696,7 +713,7 @@ qla2x00_zio_timer_store(struct device *dev, struct device_attribute *attr,
return -ERANGE;
zio_timer = (uint16_t)(val / 100);
- ha->zio_timer = zio_timer;
+ vha->hw->zio_timer = zio_timer;
return strlen(buf);
}
@@ -705,10 +722,10 @@ static ssize_t
qla2x00_beacon_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
int len = 0;
- if (ha->beacon_blink_led)
+ if (vha->hw->beacon_blink_led)
len += snprintf(buf + len, PAGE_SIZE-len, "Enabled\n");
else
len += snprintf(buf + len, PAGE_SIZE-len, "Disabled\n");
@@ -719,14 +736,15 @@ static ssize_t
qla2x00_beacon_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
int val = 0;
int rval;
if (IS_QLA2100(ha) || IS_QLA2200(ha))
return -EPERM;
- if (test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) {
+ if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) {
qla_printk(KERN_WARNING, ha,
"Abort ISP active -- ignoring beacon request.\n");
return -EBUSY;
@@ -736,9 +754,9 @@ qla2x00_beacon_store(struct device *dev, struct device_attribute *attr,
return -EINVAL;
if (val)
- rval = ha->isp_ops->beacon_on(ha);
+ rval = ha->isp_ops->beacon_on(vha);
else
- rval = ha->isp_ops->beacon_off(ha);
+ rval = ha->isp_ops->beacon_off(vha);
if (rval != QLA_SUCCESS)
count = 0;
@@ -750,8 +768,8 @@ static ssize_t
qla2x00_optrom_bios_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
-
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->bios_revision[1],
ha->bios_revision[0]);
}
@@ -760,8 +778,8 @@ static ssize_t
qla2x00_optrom_efi_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
-
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->efi_revision[1],
ha->efi_revision[0]);
}
@@ -770,8 +788,8 @@ static ssize_t
qla2x00_optrom_fcode_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
-
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->fcode_revision[1],
ha->fcode_revision[0]);
}
@@ -780,8 +798,8 @@ static ssize_t
qla2x00_optrom_fw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
-
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d %d\n",
ha->fw_revision[0], ha->fw_revision[1], ha->fw_revision[2],
ha->fw_revision[3]);
@@ -791,8 +809,8 @@ static ssize_t
qla2x00_total_isp_aborts_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
-
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
return snprintf(buf, PAGE_SIZE, "%d\n",
ha->qla_stats.total_isp_aborts);
}
@@ -848,16 +866,17 @@ struct device_attribute *qla2x00_host_attrs[] = {
static void
qla2x00_get_host_port_id(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
- fc_host_port_id(shost) = ha->d_id.b.domain << 16 |
- ha->d_id.b.area << 8 | ha->d_id.b.al_pa;
+ fc_host_port_id(shost) = vha->d_id.b.domain << 16 |
+ vha->d_id.b.area << 8 | vha->d_id.b.al_pa;
}
static void
qla2x00_get_host_speed(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = to_qla_parent(shost_priv(shost));
+ struct qla_hw_data *ha = ((struct scsi_qla_host *)
+ (shost_priv(shost)))->hw;
u32 speed = FC_PORTSPEED_UNKNOWN;
switch (ha->link_data_rate) {
@@ -880,14 +899,14 @@ qla2x00_get_host_speed(struct Scsi_Host *shost)
static void
qla2x00_get_host_port_type(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
uint32_t port_type = FC_PORTTYPE_UNKNOWN;
- if (ha->parent) {
+ if (vha->vp_idx) {
fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
return;
}
- switch (ha->current_topology) {
+ switch (vha->hw->current_topology) {
case ISP_CFG_NL:
port_type = FC_PORTTYPE_LPORT;
break;
@@ -908,11 +927,11 @@ static void
qla2x00_get_starget_node_name(struct scsi_target *starget)
{
struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
- scsi_qla_host_t *ha = shost_priv(host);
+ scsi_qla_host_t *vha = shost_priv(host);
fc_port_t *fcport;
u64 node_name = 0;
- list_for_each_entry(fcport, &ha->fcports, list) {
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (fcport->rport &&
starget->id == fcport->rport->scsi_target_id) {
node_name = wwn_to_u64(fcport->node_name);
@@ -927,11 +946,11 @@ static void
qla2x00_get_starget_port_name(struct scsi_target *starget)
{
struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
- scsi_qla_host_t *ha = shost_priv(host);
+ scsi_qla_host_t *vha = shost_priv(host);
fc_port_t *fcport;
u64 port_name = 0;
- list_for_each_entry(fcport, &ha->fcports, list) {
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (fcport->rport &&
starget->id == fcport->rport->scsi_target_id) {
port_name = wwn_to_u64(fcport->port_name);
@@ -946,11 +965,11 @@ static void
qla2x00_get_starget_port_id(struct scsi_target *starget)
{
struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
- scsi_qla_host_t *ha = shost_priv(host);
+ scsi_qla_host_t *vha = shost_priv(host);
fc_port_t *fcport;
uint32_t port_id = ~0U;
- list_for_each_entry(fcport, &ha->fcports, list) {
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (fcport->rport &&
starget->id == fcport->rport->scsi_target_id) {
port_id = fcport->d_id.b.domain << 16 |
@@ -999,9 +1018,9 @@ qla2x00_terminate_rport_io(struct fc_rport *rport)
* final cleanup of firmware resources (PCBs and XCBs).
*/
if (fcport->loop_id != FC_NO_LOOP_ID) {
- fcport->ha->isp_ops->fabric_logout(fcport->ha, fcport->loop_id,
- fcport->d_id.b.domain, fcport->d_id.b.area,
- fcport->d_id.b.al_pa);
+ fcport->vha->hw->isp_ops->fabric_logout(fcport->vha,
+ fcport->loop_id, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa);
fcport->loop_id = FC_NO_LOOP_ID;
}
@@ -1011,16 +1030,18 @@ qla2x00_terminate_rport_io(struct fc_rport *rport)
static int
qla2x00_issue_lip(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
- qla2x00_loop_reset(ha);
+ qla2x00_loop_reset(vha);
return 0;
}
static struct fc_host_statistics *
qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = to_qla_parent(shost_priv(shost));
+ scsi_qla_host_t *vha = shost_priv(shost);
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
int rval;
struct link_statistics *stats;
dma_addr_t stats_dma;
@@ -1032,21 +1053,21 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
stats = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &stats_dma);
if (stats == NULL) {
DEBUG2_3_11(printk("%s(%ld): Failed to allocate memory.\n",
- __func__, ha->host_no));
+ __func__, base_vha->host_no));
goto done;
}
memset(stats, 0, DMA_POOL_SIZE);
rval = QLA_FUNCTION_FAILED;
if (IS_FWI2_CAPABLE(ha)) {
- rval = qla24xx_get_isp_stats(ha, stats, stats_dma);
- } else if (atomic_read(&ha->loop_state) == LOOP_READY &&
- !test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) &&
- !test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) &&
+ rval = qla24xx_get_isp_stats(base_vha, stats, stats_dma);
+ } else if (atomic_read(&base_vha->loop_state) == LOOP_READY &&
+ !test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) &&
+ !test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags) &&
!ha->dpc_active) {
/* Must be in a 'READY' state for statistics retrieval. */
- rval = qla2x00_get_link_status(ha, ha->loop_id, stats,
- stats_dma);
+ rval = qla2x00_get_link_status(base_vha, base_vha->loop_id,
+ stats, stats_dma);
}
if (rval != QLA_SUCCESS)
@@ -1077,29 +1098,29 @@ done:
static void
qla2x00_get_host_symbolic_name(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
- qla2x00_get_sym_node_name(ha, fc_host_symbolic_name(shost));
+ qla2x00_get_sym_node_name(vha, fc_host_symbolic_name(shost));
}
static void
qla2x00_set_host_system_hostname(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
- set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
}
static void
qla2x00_get_host_fabric_name(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
u64 node_name;
- if (ha->device_flags & SWITCH_FOUND)
- node_name = wwn_to_u64(ha->fabric_node_name);
+ if (vha->device_flags & SWITCH_FOUND)
+ node_name = wwn_to_u64(vha->fabric_node_name);
else
- node_name = wwn_to_u64(ha->node_name);
+ node_name = wwn_to_u64(vha->node_name);
fc_host_fabric_name(shost) = node_name;
}
@@ -1107,11 +1128,12 @@ qla2x00_get_host_fabric_name(struct Scsi_Host *shost)
static void
qla2x00_get_host_port_state(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = to_qla_parent(shost_priv(shost));
+ scsi_qla_host_t *vha = shost_priv(shost);
+ struct scsi_qla_host *base_vha = pci_get_drvdata(vha->hw->pdev);
- if (!ha->flags.online)
+ if (!base_vha->flags.online)
fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
- else if (atomic_read(&ha->loop_state) == LOOP_TIMEOUT)
+ else if (atomic_read(&base_vha->loop_state) == LOOP_TIMEOUT)
fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
else
fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
@@ -1121,8 +1143,11 @@ static int
qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
{
int ret = 0;
- scsi_qla_host_t *ha = shost_priv(fc_vport->shost);
- scsi_qla_host_t *vha;
+ int cnt = 0;
+ uint8_t qos = QLA_DEFAULT_QUE_QOS;
+ scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost);
+ scsi_qla_host_t *vha = NULL;
+ struct qla_hw_data *ha = base_vha->hw;
ret = qla24xx_vport_create_req_sanity_check(fc_vport);
if (ret) {
@@ -1144,18 +1169,19 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
atomic_set(&vha->vp_state, VP_FAILED);
/* ready to create vport */
- qla_printk(KERN_INFO, vha, "VP entry id %d assigned.\n", vha->vp_idx);
+ qla_printk(KERN_INFO, vha->hw, "VP entry id %d assigned.\n",
+ vha->vp_idx);
/* initialized vport states */
atomic_set(&vha->loop_state, LOOP_DOWN);
vha->vp_err_state= VP_ERR_PORTDWN;
vha->vp_prev_err_state= VP_ERR_UNKWN;
/* Check if physical ha port is Up */
- if (atomic_read(&ha->loop_state) == LOOP_DOWN ||
- atomic_read(&ha->loop_state) == LOOP_DEAD) {
+ if (atomic_read(&base_vha->loop_state) == LOOP_DOWN ||
+ atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
/* Don't retry or attempt login of this virtual port */
DEBUG15(printk ("scsi(%ld): pport loop_state is not UP.\n",
- vha->host_no));
+ base_vha->host_no));
atomic_set(&vha->loop_state, LOOP_DEAD);
if (!disable)
fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
@@ -1171,18 +1197,32 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name);
fc_host_supported_classes(vha->host) =
- fc_host_supported_classes(ha->host);
+ fc_host_supported_classes(base_vha->host);
fc_host_supported_speeds(vha->host) =
- fc_host_supported_speeds(ha->host);
+ fc_host_supported_speeds(base_vha->host);
qla24xx_vport_disable(fc_vport, disable);
+ /* Create a queue pair for the vport */
+ if (ha->mqenable) {
+ if (ha->npiv_info) {
+ for (; cnt < ha->nvram_npiv_size; cnt++) {
+ if (ha->npiv_info[cnt].port_name ==
+ vha->port_name &&
+ ha->npiv_info[cnt].node_name ==
+ vha->node_name) {
+ qos = ha->npiv_info[cnt].q_qos;
+ break;
+ }
+ }
+ }
+ qla25xx_create_queues(vha, qos);
+ }
+
return 0;
vport_create_failed_2:
qla24xx_disable_vp(vha);
qla24xx_deallocate_vp_id(vha);
- kfree(vha->port_name);
- kfree(vha->node_name);
scsi_host_put(vha->host);
return FC_VPORT_FAILED;
}
@@ -1191,17 +1231,34 @@ static int
qla24xx_vport_delete(struct fc_vport *fc_vport)
{
scsi_qla_host_t *vha = fc_vport->dd_data;
- scsi_qla_host_t *pha = to_qla_parent(vha);
+ fc_port_t *fcport, *tfcport;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t id = vha->vp_idx;
while (test_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags) ||
- test_bit(FCPORT_UPDATE_NEEDED, &pha->dpc_flags))
+ test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags))
msleep(1000);
+ if (ha->mqenable) {
+ if (qla25xx_delete_queues(vha, 0) != QLA_SUCCESS)
+ qla_printk(KERN_WARNING, ha,
+ "Queue delete failed.\n");
+ vha->req_ques[0] = ha->req_q_map[0]->id;
+ }
+
qla24xx_disable_vp(vha);
- qla24xx_deallocate_vp_id(vha);
- kfree(vha->node_name);
- kfree(vha->port_name);
+ fc_remove_host(vha->host);
+
+ scsi_remove_host(vha->host);
+
+ list_for_each_entry_safe(fcport, tfcport, &vha->vp_fcports, list) {
+ list_del(&fcport->list);
+ kfree(fcport);
+ fcport = NULL;
+ }
+
+ qla24xx_deallocate_vp_id(vha);
if (vha->timer_active) {
qla2x00_vp_stop_timer(vha);
@@ -1210,12 +1267,8 @@ qla24xx_vport_delete(struct fc_vport *fc_vport)
vha->host_no, vha->vp_idx, vha));
}
- fc_remove_host(vha->host);
-
- scsi_remove_host(vha->host);
-
scsi_host_put(vha->host);
-
+ qla_printk(KERN_INFO, ha, "vport %d deleted\n", id);
return 0;
}
@@ -1318,15 +1371,16 @@ struct fc_function_template qla2xxx_transport_vport_functions = {
};
void
-qla2x00_init_host_attr(scsi_qla_host_t *ha)
+qla2x00_init_host_attr(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
u32 speed = FC_PORTSPEED_UNKNOWN;
- fc_host_node_name(ha->host) = wwn_to_u64(ha->node_name);
- fc_host_port_name(ha->host) = wwn_to_u64(ha->port_name);
- fc_host_supported_classes(ha->host) = FC_COS_CLASS3;
- fc_host_max_npiv_vports(ha->host) = ha->max_npiv_vports;;
- fc_host_npiv_vports_inuse(ha->host) = ha->cur_vport_count;
+ fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
+ fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name);
+ fc_host_supported_classes(vha->host) = FC_COS_CLASS3;
+ fc_host_max_npiv_vports(vha->host) = ha->max_npiv_vports;
+ fc_host_npiv_vports_inuse(vha->host) = ha->cur_vport_count;
if (IS_QLA25XX(ha))
speed = FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT |
@@ -1338,5 +1392,5 @@ qla2x00_init_host_attr(scsi_qla_host_t *ha)
speed = FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT;
else
speed = FC_PORTSPEED_1GBIT;
- fc_host_supported_speeds(ha->host) = speed;
+ fc_host_supported_speeds(vha->host) = speed;
}
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index 510ba64bc286..1cf77772623b 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -9,7 +9,7 @@
#include <linux/delay.h>
static inline void
-qla2xxx_prep_dump(scsi_qla_host_t *ha, struct qla2xxx_fw_dump *fw_dump)
+qla2xxx_prep_dump(struct qla_hw_data *ha, struct qla2xxx_fw_dump *fw_dump)
{
fw_dump->fw_major_version = htonl(ha->fw_major_version);
fw_dump->fw_minor_version = htonl(ha->fw_minor_version);
@@ -23,22 +23,24 @@ qla2xxx_prep_dump(scsi_qla_host_t *ha, struct qla2xxx_fw_dump *fw_dump)
}
static inline void *
-qla2xxx_copy_queues(scsi_qla_host_t *ha, void *ptr)
+qla2xxx_copy_queues(struct qla_hw_data *ha, void *ptr)
{
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
/* Request queue. */
- memcpy(ptr, ha->request_ring, ha->request_q_length *
+ memcpy(ptr, req->ring, req->length *
sizeof(request_t));
/* Response queue. */
- ptr += ha->request_q_length * sizeof(request_t);
- memcpy(ptr, ha->response_ring, ha->response_q_length *
+ ptr += req->length * sizeof(request_t);
+ memcpy(ptr, rsp->ring, rsp->length *
sizeof(response_t));
- return ptr + (ha->response_q_length * sizeof(response_t));
+ return ptr + (rsp->length * sizeof(response_t));
}
static int
-qla24xx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint32_t *ram,
+qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram,
uint32_t ram_dwords, void **nxt)
{
int rval;
@@ -112,7 +114,7 @@ qla24xx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint32_t *ram,
}
static int
-qla24xx_dump_memory(scsi_qla_host_t *ha, uint32_t *code_ram,
+qla24xx_dump_memory(struct qla_hw_data *ha, uint32_t *code_ram,
uint32_t cram_size, void **nxt)
{
int rval;
@@ -163,7 +165,7 @@ qla24xx_pause_risc(struct device_reg_24xx __iomem *reg)
}
static int
-qla24xx_soft_reset(scsi_qla_host_t *ha)
+qla24xx_soft_reset(struct qla_hw_data *ha)
{
int rval = QLA_SUCCESS;
uint32_t cnt;
@@ -215,8 +217,8 @@ qla24xx_soft_reset(scsi_qla_host_t *ha)
}
static int
-qla2xxx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint16_t *ram,
- uint32_t ram_words, void **nxt)
+qla2xxx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint16_t *ram,
+ uint16_t ram_words, void **nxt)
{
int rval;
uint32_t cnt, stat, timer, words, idx;
@@ -314,16 +316,17 @@ qla2xxx_read_window(struct device_reg_2xxx __iomem *reg, uint32_t count,
* @hardware_locked: Called with the hardware_lock
*/
void
-qla2300_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
+qla2300_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
{
int rval;
uint32_t cnt;
-
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
uint16_t __iomem *dmp_reg;
unsigned long flags;
struct qla2300_fw_dump *fw;
void *nxt;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
flags = 0;
@@ -468,7 +471,7 @@ qla2300_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
} else {
qla_printk(KERN_INFO, ha,
"Firmware dump saved to temp buffer (%ld/%p).\n",
- ha->host_no, ha->fw_dump);
+ base_vha->host_no, ha->fw_dump);
ha->fw_dumped = 1;
}
@@ -483,16 +486,18 @@ qla2300_fw_dump_failed:
* @hardware_locked: Called with the hardware_lock
*/
void
-qla2100_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
+qla2100_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
{
int rval;
uint32_t cnt, timer;
uint16_t risc_address;
uint16_t mb0, mb2;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
uint16_t __iomem *dmp_reg;
unsigned long flags;
struct qla2100_fw_dump *fw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
risc_address = 0;
mb0 = mb2 = 0;
@@ -673,7 +678,7 @@ qla2100_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
} else {
qla_printk(KERN_INFO, ha,
"Firmware dump saved to temp buffer (%ld/%p).\n",
- ha->host_no, ha->fw_dump);
+ base_vha->host_no, ha->fw_dump);
ha->fw_dumped = 1;
}
@@ -683,12 +688,12 @@ qla2100_fw_dump_failed:
}
void
-qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
+qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
{
int rval;
uint32_t cnt;
uint32_t risc_address;
-
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
uint32_t __iomem *dmp_reg;
uint32_t *iter_reg;
@@ -697,6 +702,7 @@ qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
struct qla24xx_fw_dump *fw;
uint32_t ext_mem_cnt;
void *nxt;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
risc_address = ext_mem_cnt = 0;
flags = 0;
@@ -919,7 +925,7 @@ qla24xx_fw_dump_failed_0:
} else {
qla_printk(KERN_INFO, ha,
"Firmware dump saved to temp buffer (%ld/%p).\n",
- ha->host_no, ha->fw_dump);
+ base_vha->host_no, ha->fw_dump);
ha->fw_dumped = 1;
}
@@ -929,13 +935,14 @@ qla24xx_fw_dump_failed:
}
void
-qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
+qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
{
int rval;
uint32_t cnt;
uint32_t risc_address;
-
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ struct device_reg_25xxmq __iomem *reg25;
uint32_t __iomem *dmp_reg;
uint32_t *iter_reg;
uint16_t __iomem *mbx_reg;
@@ -944,6 +951,11 @@ qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
uint32_t ext_mem_cnt;
void *nxt;
struct qla2xxx_fce_chain *fcec;
+ struct qla2xxx_mq_chain *mq = NULL;
+ uint32_t qreg_size;
+ uint8_t req_cnt, rsp_cnt, que_cnt;
+ uint32_t que_idx;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
risc_address = ext_mem_cnt = 0;
flags = 0;
@@ -988,6 +1000,29 @@ qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
fw->pcie_regs[1] = htonl(RD_REG_DWORD(dmp_reg++));
fw->pcie_regs[2] = htonl(RD_REG_DWORD(dmp_reg));
fw->pcie_regs[3] = htonl(RD_REG_DWORD(&reg->iobase_window));
+
+ /* Multi queue registers */
+ if (ha->mqenable) {
+ qreg_size = sizeof(struct qla2xxx_mq_chain);
+ mq = kzalloc(qreg_size, GFP_KERNEL);
+ if (!mq)
+ goto qla25xx_fw_dump_failed_0;
+ req_cnt = find_first_zero_bit(ha->req_qid_map, ha->max_queues);
+ rsp_cnt = find_first_zero_bit(ha->rsp_qid_map, ha->max_queues);
+ que_cnt = req_cnt > rsp_cnt ? req_cnt : rsp_cnt;
+ mq->count = htonl(que_cnt);
+ mq->chain_size = htonl(qreg_size);
+ mq->type = __constant_htonl(DUMP_CHAIN_MQ);
+ for (cnt = 0; cnt < que_cnt; cnt++) {
+ reg25 = (struct device_reg_25xxmq *) ((void *)
+ ha->mqiobase + cnt * QLA_QUE_PAGE);
+ que_idx = cnt * 4;
+ mq->qregs[que_idx] = htonl(reg25->req_q_in);
+ mq->qregs[que_idx+1] = htonl(reg25->req_q_out);
+ mq->qregs[que_idx+2] = htonl(reg25->rsp_q_in);
+ mq->qregs[que_idx+3] = htonl(reg25->rsp_q_out);
+ }
+ }
WRT_REG_DWORD(&reg->iobase_window, 0x00);
RD_REG_DWORD(&reg->iobase_window);
@@ -1225,7 +1260,14 @@ qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
- fcec = nxt + ntohl(ha->fw_dump->eft_size);
+ if (ha->mqenable) {
+ nxt = nxt + ntohl(ha->fw_dump->eft_size);
+ memcpy(nxt, mq, qreg_size);
+ kfree(mq);
+ fcec = nxt + qreg_size;
+ } else {
+ fcec = nxt + ntohl(ha->fw_dump->eft_size);
+ }
fcec->type = __constant_htonl(DUMP_CHAIN_FCE | DUMP_CHAIN_LAST);
fcec->chain_size = htonl(sizeof(struct qla2xxx_fce_chain) +
fce_calc_size(ha->fce_bufs));
@@ -1248,7 +1290,7 @@ qla25xx_fw_dump_failed_0:
} else {
qla_printk(KERN_INFO, ha,
"Firmware dump saved to temp buffer (%ld/%p).\n",
- ha->host_no, ha->fw_dump);
+ base_vha->host_no, ha->fw_dump);
ha->fw_dumped = 1;
}
@@ -1256,15 +1298,15 @@ qla25xx_fw_dump_failed:
if (!hardware_locked)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
-
/****************************************************************************/
/* Driver Debug Functions. */
/****************************************************************************/
void
-qla2x00_dump_regs(scsi_qla_host_t *ha)
+qla2x00_dump_regs(scsi_qla_host_t *vha)
{
int i;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
uint16_t __iomem *mbx_reg;
@@ -1274,7 +1316,7 @@ qla2x00_dump_regs(scsi_qla_host_t *ha)
printk("Mailbox registers:\n");
for (i = 0; i < 6; i++)
- printk("scsi(%ld): mbox %d 0x%04x \n", ha->host_no, i,
+ printk("scsi(%ld): mbox %d 0x%04x \n", vha->host_no, i,
RD_REG_WORD(mbx_reg++));
}
@@ -1302,3 +1344,5 @@ qla2x00_dump_buffer(uint8_t * b, uint32_t size)
if (cnt % 16)
printk("\n");
}
+
+
diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h
index 2e9c0c097f5e..c1794a70a45f 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.h
+++ b/drivers/scsi/qla2xxx/qla_dbg.h
@@ -4,6 +4,9 @@
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
+
+#include "qla_def.h"
+
/*
* Driver debug definitions.
*/
@@ -23,6 +26,7 @@
/* #define QL_DEBUG_LEVEL_14 */ /* Output RSCN trace msgs */
/* #define QL_DEBUG_LEVEL_15 */ /* Output NPIV trace msgs */
/* #define QL_DEBUG_LEVEL_16 */ /* Output ISP84XX trace msgs */
+/* #define QL_DEBUG_LEVEL_17 */ /* Output MULTI-Q trace messages */
/*
* Macros use for debugging the driver.
@@ -43,6 +47,7 @@
#define DEBUG2_11(x) do { if (ql2xextended_error_logging) { x; } } while (0)
#define DEBUG2_13(x) do { if (ql2xextended_error_logging) { x; } } while (0)
#define DEBUG2_16(x) do { if (ql2xextended_error_logging) { x; } } while (0)
+#define DEBUG2_17(x) do { if (ql2xextended_error_logging) { x; } } while (0)
#if defined(QL_DEBUG_LEVEL_3)
#define DEBUG3(x) do {x;} while (0)
@@ -127,7 +132,6 @@
#else
#define DEBUG16(x) do {} while (0)
#endif
-
/*
* Firmware Dump structure definition
*/
@@ -266,8 +270,17 @@ struct qla2xxx_fce_chain {
uint32_t eregs[8];
};
+struct qla2xxx_mq_chain {
+ uint32_t type;
+ uint32_t chain_size;
+
+ uint32_t count;
+ uint32_t qregs[4 * QLA_MQ_SIZE];
+};
+
#define DUMP_CHAIN_VARIANT 0x80000000
#define DUMP_CHAIN_FCE 0x7FFFFAF0
+#define DUMP_CHAIN_MQ 0x7FFFFAF1
#define DUMP_CHAIN_LAST 0x80000000
struct qla2xxx_fw_dump {
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index b97194096d8e..a29c95204975 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -181,11 +181,14 @@
#define RESPONSE_ENTRY_CNT_2100 64 /* Number of response entries.*/
#define RESPONSE_ENTRY_CNT_2300 512 /* Number of response entries.*/
+struct req_que;
+
/*
* SCSI Request Block
*/
typedef struct srb {
- struct scsi_qla_host *ha; /* HA the SP is queued on */
+ struct scsi_qla_host *vha; /* HA the SP is queued on */
+ struct req_que *que;
struct fc_port *fcport;
struct scsi_cmnd *cmd; /* Linux SCSI command pkt */
@@ -369,9 +372,17 @@ struct device_reg_2xxx {
} u_end;
};
+struct device_reg_25xxmq {
+ volatile uint32_t req_q_in;
+ volatile uint32_t req_q_out;
+ volatile uint32_t rsp_q_in;
+ volatile uint32_t rsp_q_out;
+};
+
typedef union {
struct device_reg_2xxx isp;
struct device_reg_24xx isp24;
+ struct device_reg_25xxmq isp25mq;
} device_reg_t;
#define ISP_REQ_Q_IN(ha, reg) \
@@ -1524,7 +1535,7 @@ typedef struct {
*/
typedef struct fc_port {
struct list_head list;
- struct scsi_qla_host *ha;
+ struct scsi_qla_host *vha;
uint8_t node_name[WWN_SIZE];
uint8_t port_name[WWN_SIZE];
@@ -1550,7 +1561,6 @@ typedef struct fc_port {
unsigned long last_queue_full;
unsigned long last_ramp_up;
- struct list_head vp_fcport;
uint16_t vp_idx;
} fc_port_t;
@@ -2037,6 +2047,8 @@ typedef struct vport_params {
#define VP_RET_CODE_NO_MEM 5
#define VP_RET_CODE_NOT_FOUND 6
+struct qla_hw_data;
+
/*
* ISP operations
*/
@@ -2055,10 +2067,11 @@ struct isp_operations {
char * (*fw_version_str) (struct scsi_qla_host *, char *);
irq_handler_t intr_handler;
- void (*enable_intrs) (struct scsi_qla_host *);
- void (*disable_intrs) (struct scsi_qla_host *);
+ void (*enable_intrs) (struct qla_hw_data *);
+ void (*disable_intrs) (struct qla_hw_data *);
- int (*abort_command) (struct scsi_qla_host *, srb_t *);
+ int (*abort_command) (struct scsi_qla_host *, srb_t *,
+ struct req_que *);
int (*target_reset) (struct fc_port *, unsigned int);
int (*lun_reset) (struct fc_port *, unsigned int);
int (*fabric_login) (struct scsi_qla_host *, uint16_t, uint8_t,
@@ -2089,6 +2102,10 @@ struct isp_operations {
uint32_t);
int (*get_flash_version) (struct scsi_qla_host *, void *);
+ int (*start_scsi) (srb_t *);
+ void (*wrt_req_reg) (struct qla_hw_data *, uint16_t, uint16_t);
+ void (*wrt_rsp_reg) (struct qla_hw_data *, uint16_t, uint16_t);
+ uint16_t (*rd_req_reg) (struct qla_hw_data *, uint16_t);
};
/* MSI-X Support *************************************************************/
@@ -2100,16 +2117,18 @@ struct isp_operations {
#define QLA_MSIX_DEFAULT 0x00
#define QLA_MSIX_RSP_Q 0x01
-#define QLA_MSIX_ENTRIES 2
#define QLA_MIDX_DEFAULT 0
#define QLA_MIDX_RSP_Q 1
+#define QLA_PCI_MSIX_CONTROL 0xa2
struct scsi_qla_host;
+struct rsp_que;
struct qla_msix_entry {
int have_irq;
- uint32_t msix_vector;
- uint16_t msix_entry;
+ uint32_t vector;
+ uint16_t entry;
+ struct rsp_que *rsp;
};
#define WATCH_INTERVAL 1 /* number of seconds */
@@ -2160,208 +2179,137 @@ struct qla_statistics {
uint64_t output_bytes;
};
-/*
- * Linux Host Adapter structure
- */
-typedef struct scsi_qla_host {
- struct list_head list;
+/* Multi queue support */
+#define MBC_INITIALIZE_MULTIQ 0x1f
+#define QLA_QUE_PAGE 0X1000
+#define QLA_MQ_SIZE 32
+#define QLA_MAX_HOST_QUES 16
+#define QLA_MAX_QUEUES 256
+#define ISP_QUE_REG(ha, id) \
+ ((ha->mqenable) ? \
+ ((void *)(ha->mqiobase) +\
+ (QLA_QUE_PAGE * id)) :\
+ ((void *)(ha->iobase)))
+#define QLA_REQ_QUE_ID(tag) \
+ ((tag < QLA_MAX_QUEUES && tag > 0) ? tag : 0)
+#define QLA_DEFAULT_QUE_QOS 5
+#define QLA_PRECONFIG_VPORTS 32
+#define QLA_MAX_VPORTS_QLA24XX 128
+#define QLA_MAX_VPORTS_QLA25XX 256
+/* Response queue data structure */
+struct rsp_que {
+ dma_addr_t dma;
+ response_t *ring;
+ response_t *ring_ptr;
+ uint16_t ring_index;
+ uint16_t out_ptr;
+ uint16_t length;
+ uint16_t options;
+ uint16_t rid;
+ uint16_t id;
+ uint16_t vp_idx;
+ struct qla_hw_data *hw;
+ struct qla_msix_entry *msix;
+ struct req_que *req;
+};
- /* Commonly used flags and state information. */
- struct Scsi_Host *host;
- struct pci_dev *pdev;
+/* Request queue data structure */
+struct req_que {
+ dma_addr_t dma;
+ request_t *ring;
+ request_t *ring_ptr;
+ uint16_t ring_index;
+ uint16_t in_ptr;
+ uint16_t cnt;
+ uint16_t length;
+ uint16_t options;
+ uint16_t rid;
+ uint16_t id;
+ uint16_t qos;
+ uint16_t vp_idx;
+ struct rsp_que *rsp;
+ srb_t *outstanding_cmds[MAX_OUTSTANDING_COMMANDS];
+ uint32_t current_outstanding_cmd;
+ int max_q_depth;
+};
- unsigned long host_no;
+/*
+ * Qlogic host adapter specific data structure.
+*/
+struct qla_hw_data {
+ struct pci_dev *pdev;
+ /* SRB cache. */
+#define SRB_MIN_REQ 128
+ mempool_t *srb_mempool;
volatile struct {
- uint32_t init_done :1;
- uint32_t online :1;
uint32_t mbox_int :1;
uint32_t mbox_busy :1;
- uint32_t rscn_queue_overflow :1;
- uint32_t reset_active :1;
-
- uint32_t management_server_logged_in :1;
- uint32_t process_response_queue :1;
uint32_t disable_risc_code_load :1;
uint32_t enable_64bit_addressing :1;
uint32_t enable_lip_reset :1;
- uint32_t enable_lip_full_login :1;
uint32_t enable_target_reset :1;
+ uint32_t enable_lip_full_login :1;
uint32_t enable_led_scheme :1;
uint32_t inta_enabled :1;
uint32_t msi_enabled :1;
uint32_t msix_enabled :1;
uint32_t disable_serdes :1;
uint32_t gpsc_supported :1;
- uint32_t vsan_enabled :1;
+ uint32_t vsan_enabled :1;
uint32_t npiv_supported :1;
uint32_t fce_enabled :1;
- uint32_t hw_event_marker_found :1;
+ uint32_t hw_event_marker_found:1;
} flags;
- atomic_t loop_state;
-#define LOOP_TIMEOUT 1
-#define LOOP_DOWN 2
-#define LOOP_UP 3
-#define LOOP_UPDATE 4
-#define LOOP_READY 5
-#define LOOP_DEAD 6
-
- unsigned long dpc_flags;
-#define RESET_MARKER_NEEDED 0 /* Send marker to ISP. */
-#define RESET_ACTIVE 1
-#define ISP_ABORT_NEEDED 2 /* Initiate ISP abort. */
-#define ABORT_ISP_ACTIVE 3 /* ISP abort in progress. */
-#define LOOP_RESYNC_NEEDED 4 /* Device Resync needed. */
-#define LOOP_RESYNC_ACTIVE 5
-#define LOCAL_LOOP_UPDATE 6 /* Perform a local loop update. */
-#define RSCN_UPDATE 7 /* Perform an RSCN update. */
-#define MAILBOX_RETRY 8
-#define ISP_RESET_NEEDED 9 /* Initiate a ISP reset. */
-#define FAILOVER_EVENT_NEEDED 10
-#define FAILOVER_EVENT 11
-#define FAILOVER_NEEDED 12
-#define SCSI_RESTART_NEEDED 13 /* Processes SCSI retry queue. */
-#define PORT_RESTART_NEEDED 14 /* Processes Retry queue. */
-#define RESTART_QUEUES_NEEDED 15 /* Restarts the Lun queue. */
-#define ABORT_QUEUES_NEEDED 16
-#define RELOGIN_NEEDED 17
-#define LOGIN_RETRY_NEEDED 18 /* Initiate required fabric logins. */
-#define REGISTER_FC4_NEEDED 19 /* SNS FC4 registration required. */
-#define ISP_ABORT_RETRY 20 /* ISP aborted. */
-#define FCPORT_RESCAN_NEEDED 21 /* IO descriptor processing needed */
-#define IODESC_PROCESS_NEEDED 22 /* IO descriptor processing needed */
-#define IOCTL_ERROR_RECOVERY 23
-#define LOOP_RESET_NEEDED 24
-#define BEACON_BLINK_NEEDED 25
-#define REGISTER_FDMI_NEEDED 26
-#define FCPORT_UPDATE_NEEDED 27
-#define VP_DPC_NEEDED 28 /* wake up for VP dpc handling */
-#define UNLOADING 29
-#define NPIV_CONFIG_NEEDED 30
-
- uint32_t device_flags;
-#define DFLG_LOCAL_DEVICES BIT_0
-#define DFLG_RETRY_LOCAL_DEVICES BIT_1
-#define DFLG_FABRIC_DEVICES BIT_2
-#define SWITCH_FOUND BIT_3
-#define DFLG_NO_CABLE BIT_4
-
-#define PCI_DEVICE_ID_QLOGIC_ISP2532 0x2532
-#define PCI_DEVICE_ID_QLOGIC_ISP8432 0x8432
- uint32_t device_type;
-#define DT_ISP2100 BIT_0
-#define DT_ISP2200 BIT_1
-#define DT_ISP2300 BIT_2
-#define DT_ISP2312 BIT_3
-#define DT_ISP2322 BIT_4
-#define DT_ISP6312 BIT_5
-#define DT_ISP6322 BIT_6
-#define DT_ISP2422 BIT_7
-#define DT_ISP2432 BIT_8
-#define DT_ISP5422 BIT_9
-#define DT_ISP5432 BIT_10
-#define DT_ISP2532 BIT_11
-#define DT_ISP8432 BIT_12
-#define DT_ISP_LAST (DT_ISP8432 << 1)
-
-#define DT_IIDMA BIT_26
-#define DT_FWI2 BIT_27
-#define DT_ZIO_SUPPORTED BIT_28
-#define DT_OEM_001 BIT_29
-#define DT_ISP2200A BIT_30
-#define DT_EXTENDED_IDS BIT_31
-
-#define DT_MASK(ha) ((ha)->device_type & (DT_ISP_LAST - 1))
-#define IS_QLA2100(ha) (DT_MASK(ha) & DT_ISP2100)
-#define IS_QLA2200(ha) (DT_MASK(ha) & DT_ISP2200)
-#define IS_QLA2300(ha) (DT_MASK(ha) & DT_ISP2300)
-#define IS_QLA2312(ha) (DT_MASK(ha) & DT_ISP2312)
-#define IS_QLA2322(ha) (DT_MASK(ha) & DT_ISP2322)
-#define IS_QLA6312(ha) (DT_MASK(ha) & DT_ISP6312)
-#define IS_QLA6322(ha) (DT_MASK(ha) & DT_ISP6322)
-#define IS_QLA2422(ha) (DT_MASK(ha) & DT_ISP2422)
-#define IS_QLA2432(ha) (DT_MASK(ha) & DT_ISP2432)
-#define IS_QLA5422(ha) (DT_MASK(ha) & DT_ISP5422)
-#define IS_QLA5432(ha) (DT_MASK(ha) & DT_ISP5432)
-#define IS_QLA2532(ha) (DT_MASK(ha) & DT_ISP2532)
-#define IS_QLA8432(ha) (DT_MASK(ha) & DT_ISP8432)
-
-#define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \
- IS_QLA6312(ha) || IS_QLA6322(ha))
-#define IS_QLA24XX(ha) (IS_QLA2422(ha) || IS_QLA2432(ha))
-#define IS_QLA54XX(ha) (IS_QLA5422(ha) || IS_QLA5432(ha))
-#define IS_QLA25XX(ha) (IS_QLA2532(ha))
-#define IS_QLA84XX(ha) (IS_QLA8432(ha))
-#define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \
- IS_QLA84XX(ha))
-
-#define IS_IIDMA_CAPABLE(ha) ((ha)->device_type & DT_IIDMA)
-#define IS_FWI2_CAPABLE(ha) ((ha)->device_type & DT_FWI2)
-#define IS_ZIO_SUPPORTED(ha) ((ha)->device_type & DT_ZIO_SUPPORTED)
-#define IS_OEM_001(ha) ((ha)->device_type & DT_OEM_001)
-#define HAS_EXTENDED_IDS(ha) ((ha)->device_type & DT_EXTENDED_IDS)
-
- /* SRB cache. */
-#define SRB_MIN_REQ 128
- mempool_t *srb_mempool;
-
/* This spinlock is used to protect "io transactions", you must
- * acquire it before doing any IO to the card, eg with RD_REG*() and
- * WRT_REG*() for the duration of your entire commandtransaction.
- *
- * This spinlock is of lower priority than the io request lock.
- */
-
- spinlock_t hardware_lock ____cacheline_aligned;
+ * acquire it before doing any IO to the card, eg with RD_REG*() and
+ * WRT_REG*() for the duration of your entire commandtransaction.
+ *
+ * This spinlock is of lower priority than the io request lock.
+ */
+ spinlock_t hardware_lock ____cacheline_aligned;
int bars;
int mem_only;
- device_reg_t __iomem *iobase; /* Base I/O address */
+ device_reg_t __iomem *iobase; /* Base I/O address */
resource_size_t pio_address;
-#define MIN_IOBASE_LEN 0x100
-
- /* ISP ring lock, rings, and indexes */
- dma_addr_t request_dma; /* Physical address. */
- request_t *request_ring; /* Base virtual address */
- request_t *request_ring_ptr; /* Current address. */
- uint16_t req_ring_index; /* Current index. */
- uint16_t req_q_cnt; /* Number of available entries. */
- uint16_t request_q_length;
-
- dma_addr_t response_dma; /* Physical address. */
- response_t *response_ring; /* Base virtual address */
- response_t *response_ring_ptr; /* Current address. */
- uint16_t rsp_ring_index; /* Current index. */
- uint16_t response_q_length;
-
- struct isp_operations *isp_ops;
- /* Outstandings ISP commands. */
- srb_t *outstanding_cmds[MAX_OUTSTANDING_COMMANDS];
- uint32_t current_outstanding_cmd;
- srb_t *status_srb; /* Status continuation entry. */
+#define MIN_IOBASE_LEN 0x100
+/* Multi queue data structs */
+ device_reg_t *mqiobase;
+ uint16_t msix_count;
+ uint8_t mqenable;
+ struct req_que **req_q_map;
+ struct rsp_que **rsp_q_map;
+ unsigned long req_qid_map[(QLA_MAX_QUEUES / 8) / sizeof(unsigned long)];
+ unsigned long rsp_qid_map[(QLA_MAX_QUEUES / 8) / sizeof(unsigned long)];
+ uint16_t max_queues;
+ struct qla_npiv_entry *npiv_info;
+ uint16_t nvram_npiv_size;
+
+ uint16_t switch_cap;
+#define FLOGI_SEQ_DEL BIT_8
+#define FLOGI_MID_SUPPORT BIT_10
+#define FLOGI_VSAN_SUPPORT BIT_12
+#define FLOGI_SP_SUPPORT BIT_13
+ /* Timeout timers. */
+ uint8_t loop_down_abort_time; /* port down timer */
+ atomic_t loop_down_timer; /* loop down timer */
+ uint8_t link_down_timeout; /* link down timeout */
+ uint16_t max_loop_id;
- /* ISP configuration data. */
- uint16_t loop_id; /* Host adapter loop id */
- uint16_t switch_cap;
-#define FLOGI_SEQ_DEL BIT_8
-#define FLOGI_MID_SUPPORT BIT_10
-#define FLOGI_VSAN_SUPPORT BIT_12
-#define FLOGI_SP_SUPPORT BIT_13
uint16_t fb_rev;
-
- port_id_t d_id; /* Host adapter port id */
uint16_t max_public_loop_ids;
- uint16_t min_external_loopid; /* First external loop Id */
+ uint16_t min_external_loopid; /* First external loop Id */
#define PORT_SPEED_UNKNOWN 0xFFFF
-#define PORT_SPEED_1GB 0x00
-#define PORT_SPEED_2GB 0x01
-#define PORT_SPEED_4GB 0x03
-#define PORT_SPEED_8GB 0x04
- uint16_t link_data_rate; /* F/W operating speed */
+#define PORT_SPEED_1GB 0x00
+#define PORT_SPEED_2GB 0x01
+#define PORT_SPEED_4GB 0x03
+#define PORT_SPEED_8GB 0x04
+ uint16_t link_data_rate; /* F/W operating speed */
uint8_t current_topology;
uint8_t prev_topology;
@@ -2370,15 +2318,69 @@ typedef struct scsi_qla_host {
#define ISP_CFG_FL 4
#define ISP_CFG_F 8
- uint8_t operating_mode; /* F/W operating mode */
+ uint8_t operating_mode; /* F/W operating mode */
#define LOOP 0
#define P2P 1
#define LOOP_P2P 2
#define P2P_LOOP 3
-
- uint8_t marker_needed;
-
uint8_t interrupts_on;
+ uint32_t isp_abort_cnt;
+
+#define PCI_DEVICE_ID_QLOGIC_ISP2532 0x2532
+#define PCI_DEVICE_ID_QLOGIC_ISP8432 0x8432
+ uint32_t device_type;
+#define DT_ISP2100 BIT_0
+#define DT_ISP2200 BIT_1
+#define DT_ISP2300 BIT_2
+#define DT_ISP2312 BIT_3
+#define DT_ISP2322 BIT_4
+#define DT_ISP6312 BIT_5
+#define DT_ISP6322 BIT_6
+#define DT_ISP2422 BIT_7
+#define DT_ISP2432 BIT_8
+#define DT_ISP5422 BIT_9
+#define DT_ISP5432 BIT_10
+#define DT_ISP2532 BIT_11
+#define DT_ISP8432 BIT_12
+#define DT_ISP_LAST (DT_ISP8432 << 1)
+
+#define DT_IIDMA BIT_26
+#define DT_FWI2 BIT_27
+#define DT_ZIO_SUPPORTED BIT_28
+#define DT_OEM_001 BIT_29
+#define DT_ISP2200A BIT_30
+#define DT_EXTENDED_IDS BIT_31
+#define DT_MASK(ha) ((ha)->device_type & (DT_ISP_LAST - 1))
+#define IS_QLA2100(ha) (DT_MASK(ha) & DT_ISP2100)
+#define IS_QLA2200(ha) (DT_MASK(ha) & DT_ISP2200)
+#define IS_QLA2300(ha) (DT_MASK(ha) & DT_ISP2300)
+#define IS_QLA2312(ha) (DT_MASK(ha) & DT_ISP2312)
+#define IS_QLA2322(ha) (DT_MASK(ha) & DT_ISP2322)
+#define IS_QLA6312(ha) (DT_MASK(ha) & DT_ISP6312)
+#define IS_QLA6322(ha) (DT_MASK(ha) & DT_ISP6322)
+#define IS_QLA2422(ha) (DT_MASK(ha) & DT_ISP2422)
+#define IS_QLA2432(ha) (DT_MASK(ha) & DT_ISP2432)
+#define IS_QLA5422(ha) (DT_MASK(ha) & DT_ISP5422)
+#define IS_QLA5432(ha) (DT_MASK(ha) & DT_ISP5432)
+#define IS_QLA2532(ha) (DT_MASK(ha) & DT_ISP2532)
+#define IS_QLA8432(ha) (DT_MASK(ha) & DT_ISP8432)
+
+#define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \
+ IS_QLA6312(ha) || IS_QLA6322(ha))
+#define IS_QLA24XX(ha) (IS_QLA2422(ha) || IS_QLA2432(ha))
+#define IS_QLA54XX(ha) (IS_QLA5422(ha) || IS_QLA5432(ha))
+#define IS_QLA25XX(ha) (IS_QLA2532(ha))
+#define IS_QLA84XX(ha) (IS_QLA8432(ha))
+#define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \
+ IS_QLA84XX(ha))
+#define IS_QLA2XXX_MIDTYPE(ha) (IS_QLA24XX(ha) || IS_QLA84XX(ha) || \
+ IS_QLA25XX(ha))
+
+#define IS_IIDMA_CAPABLE(ha) ((ha)->device_type & DT_IIDMA)
+#define IS_FWI2_CAPABLE(ha) ((ha)->device_type & DT_FWI2)
+#define IS_ZIO_SUPPORTED(ha) ((ha)->device_type & DT_ZIO_SUPPORTED)
+#define IS_OEM_001(ha) ((ha)->device_type & DT_OEM_001)
+#define HAS_EXTENDED_IDS(ha) ((ha)->device_type & DT_EXTENDED_IDS)
/* HBA serial number */
uint8_t serial0;
@@ -2386,8 +2388,8 @@ typedef struct scsi_qla_host {
uint8_t serial2;
/* NVRAM configuration data */
-#define MAX_NVRAM_SIZE 4096
-#define VPD_OFFSET MAX_NVRAM_SIZE / 2
+#define MAX_NVRAM_SIZE 4096
+#define VPD_OFFSET MAX_NVRAM_SIZE / 2
uint16_t nvram_size;
uint16_t nvram_base;
void *nvram;
@@ -2401,22 +2403,8 @@ typedef struct scsi_qla_host {
uint16_t r_a_tov;
int port_down_retry_count;
uint8_t mbx_count;
- uint16_t last_loop_id;
- uint16_t mgmt_svr_loop_id;
-
- uint32_t login_retry_count;
- int max_q_depth;
-
- struct list_head work_list;
-
- /* Fibre Channel Device List. */
- struct list_head fcports;
-
- /* RSCN queue. */
- uint32_t rscn_queue[MAX_RSCN_COUNT];
- uint8_t rscn_in_ptr;
- uint8_t rscn_out_ptr;
+ uint32_t login_retry_count;
/* SNS command interfaces. */
ms_iocb_entry_t *ms_iocb;
dma_addr_t ms_iocb_dma;
@@ -2426,28 +2414,20 @@ typedef struct scsi_qla_host {
struct sns_cmd_pkt *sns_cmd;
dma_addr_t sns_cmd_dma;
-#define SFP_DEV_SIZE 256
-#define SFP_BLOCK_SIZE 64
- void *sfp_data;
- dma_addr_t sfp_data_dma;
+#define SFP_DEV_SIZE 256
+#define SFP_BLOCK_SIZE 64
+ void *sfp_data;
+ dma_addr_t sfp_data_dma;
struct task_struct *dpc_thread;
uint8_t dpc_active; /* DPC routine is active */
- /* Timeout timers. */
- uint8_t loop_down_abort_time; /* port down timer */
- atomic_t loop_down_timer; /* loop down timer */
- uint8_t link_down_timeout; /* link down timeout */
-
- uint32_t timer_active;
- struct timer_list timer;
-
dma_addr_t gid_list_dma;
struct gid_list_info *gid_list;
int gid_list_info_size;
/* Small DMA pool allocations -- maximum 256 bytes in length. */
-#define DMA_POOL_SIZE 256
+#define DMA_POOL_SIZE 256
struct dma_pool *s_dma_pool;
dma_addr_t init_cb_dma;
@@ -2459,17 +2439,17 @@ typedef struct scsi_qla_host {
mbx_cmd_t *mcp;
unsigned long mbx_cmd_flags;
-#define MBX_INTERRUPT 1
-#define MBX_INTR_WAIT 2
+#define MBX_INTERRUPT 1
+#define MBX_INTR_WAIT 2
#define MBX_UPDATE_FLASH_ACTIVE 3
- struct mutex vport_lock; /* Virtual port synchronization */
- struct completion mbx_cmd_comp; /* Serialize mbx access */
+ struct mutex vport_lock; /* Virtual port synchronization */
+ struct completion mbx_cmd_comp; /* Serialize mbx access */
struct completion mbx_intr_comp; /* Used for completion notification */
uint32_t mbx_flags;
#define MBX_IN_PROGRESS BIT_0
-#define MBX_BUSY BIT_1 /* Got the Access */
+#define MBX_BUSY BIT_1 /* Got the Access */
#define MBX_SLEEPING_ON_SEM BIT_2
#define MBX_POLLING_FOR_COMP BIT_3
#define MBX_COMPLETED BIT_4
@@ -2488,7 +2468,7 @@ typedef struct scsi_qla_host {
#define RISC_START_ADDRESS_2300 0x800
#define RISC_START_ADDRESS_2400 0x100000
- uint16_t fw_options[16]; /* slots: 1,2,3,10,11 */
+ uint16_t fw_options[16]; /* slots: 1,2,3,10,11 */
uint8_t fw_seriallink_options[4];
uint16_t fw_seriallink_options24[4];
@@ -2509,10 +2489,10 @@ typedef struct scsi_qla_host {
uint64_t fce_wr, fce_rd;
struct mutex fce_mutex;
+ uint32_t hw_event_start;
uint32_t hw_event_ptr;
uint32_t hw_event_pause_errors;
- uint8_t host_str[16];
uint32_t pci_attr;
uint16_t chip_revision;
@@ -2523,11 +2503,6 @@ typedef struct scsi_qla_host {
char model_desc[80];
uint8_t adapter_id[16+1];
- uint8_t *node_name;
- uint8_t *port_name;
- uint8_t fabric_node_name[WWN_SIZE];
- uint32_t isp_abort_cnt;
-
/* Option ROM information. */
char *optrom_buffer;
uint32_t optrom_size;
@@ -2538,13 +2513,13 @@ typedef struct scsi_qla_host {
uint32_t optrom_region_start;
uint32_t optrom_region_size;
- /* PCI expansion ROM image information. */
+/* PCI expansion ROM image information. */
#define ROM_CODE_TYPE_BIOS 0
#define ROM_CODE_TYPE_FCODE 1
#define ROM_CODE_TYPE_EFI 3
- uint8_t bios_revision[2];
- uint8_t efi_revision[2];
- uint8_t fcode_revision[16];
+ uint8_t bios_revision[2];
+ uint8_t efi_revision[2];
+ uint8_t fcode_revision[16];
uint32_t fw_revision[4];
uint32_t fdt_wrt_disable;
@@ -2553,39 +2528,144 @@ typedef struct scsi_qla_host {
uint32_t fdt_unprotect_sec_cmd;
uint32_t fdt_protect_sec_cmd;
- uint32_t flt_region_flt;
- uint32_t flt_region_fdt;
- uint32_t flt_region_boot;
- uint32_t flt_region_fw;
- uint32_t flt_region_vpd_nvram;
- uint32_t flt_region_hw_event;
- uint32_t flt_region_npiv_conf;
+ uint32_t flt_region_flt;
+ uint32_t flt_region_fdt;
+ uint32_t flt_region_boot;
+ uint32_t flt_region_fw;
+ uint32_t flt_region_vpd_nvram;
+ uint32_t flt_region_hw_event;
+ uint32_t flt_region_npiv_conf;
/* Needed for BEACON */
- uint16_t beacon_blink_led;
- uint8_t beacon_color_state;
+ uint16_t beacon_blink_led;
+ uint8_t beacon_color_state;
#define QLA_LED_GRN_ON 0x01
#define QLA_LED_YLW_ON 0x02
#define QLA_LED_ABR_ON 0x04
#define QLA_LED_ALL_ON 0x07 /* yellow, green, amber. */
/* ISP2322: red, green, amber. */
-
- uint16_t zio_mode;
- uint16_t zio_timer;
+ uint16_t zio_mode;
+ uint16_t zio_timer;
struct fc_host_statistics fc_host_stat;
- struct qla_msix_entry msix_entries[QLA_MSIX_ENTRIES];
+ struct qla_msix_entry *msix_entries;
+
+ struct list_head vp_list; /* list of VP */
+ unsigned long vp_idx_map[(MAX_MULTI_ID_FABRIC / 8) /
+ sizeof(unsigned long)];
+ uint16_t num_vhosts; /* number of vports created */
+ uint16_t num_vsans; /* number of vsan created */
+ uint16_t max_npiv_vports; /* 63 or 125 per topoloty */
+ int cur_vport_count;
+
+ struct qla_chip_state_84xx *cs84xx;
+ struct qla_statistics qla_stats;
+ struct isp_operations *isp_ops;
+};
+
+/*
+ * Qlogic scsi host structure
+ */
+typedef struct scsi_qla_host {
+ struct list_head list;
+ struct list_head vp_fcports; /* list of fcports */
+ struct list_head work_list;
+ /* Commonly used flags and state information. */
+ struct Scsi_Host *host;
+ unsigned long host_no;
+ uint8_t host_str[16];
+
+ volatile struct {
+ uint32_t init_done :1;
+ uint32_t online :1;
+ uint32_t rscn_queue_overflow :1;
+ uint32_t reset_active :1;
+
+ uint32_t management_server_logged_in :1;
+ uint32_t process_response_queue :1;
+ } flags;
+
+ atomic_t loop_state;
+#define LOOP_TIMEOUT 1
+#define LOOP_DOWN 2
+#define LOOP_UP 3
+#define LOOP_UPDATE 4
+#define LOOP_READY 5
+#define LOOP_DEAD 6
+
+ unsigned long dpc_flags;
+#define RESET_MARKER_NEEDED 0 /* Send marker to ISP. */
+#define RESET_ACTIVE 1
+#define ISP_ABORT_NEEDED 2 /* Initiate ISP abort. */
+#define ABORT_ISP_ACTIVE 3 /* ISP abort in progress. */
+#define LOOP_RESYNC_NEEDED 4 /* Device Resync needed. */
+#define LOOP_RESYNC_ACTIVE 5
+#define LOCAL_LOOP_UPDATE 6 /* Perform a local loop update. */
+#define RSCN_UPDATE 7 /* Perform an RSCN update. */
+#define MAILBOX_RETRY 8
+#define ISP_RESET_NEEDED 9 /* Initiate a ISP reset. */
+#define FAILOVER_EVENT_NEEDED 10
+#define FAILOVER_EVENT 11
+#define FAILOVER_NEEDED 12
+#define SCSI_RESTART_NEEDED 13 /* Processes SCSI retry queue. */
+#define PORT_RESTART_NEEDED 14 /* Processes Retry queue. */
+#define RESTART_QUEUES_NEEDED 15 /* Restarts the Lun queue. */
+#define ABORT_QUEUES_NEEDED 16
+#define RELOGIN_NEEDED 17
+#define LOGIN_RETRY_NEEDED 18 /* Initiate required fabric logins. */
+#define REGISTER_FC4_NEEDED 19 /* SNS FC4 registration required. */
+#define ISP_ABORT_RETRY 20 /* ISP aborted. */
+#define FCPORT_RESCAN_NEEDED 21 /* IO descriptor processing needed */
+#define IODESC_PROCESS_NEEDED 22 /* IO descriptor processing needed */
+#define IOCTL_ERROR_RECOVERY 23
+#define LOOP_RESET_NEEDED 24
+#define BEACON_BLINK_NEEDED 25
+#define REGISTER_FDMI_NEEDED 26
+#define FCPORT_UPDATE_NEEDED 27
+#define VP_DPC_NEEDED 28 /* wake up for VP dpc handling */
+#define UNLOADING 29
+#define NPIV_CONFIG_NEEDED 30
+
+ uint32_t device_flags;
+#define DFLG_LOCAL_DEVICES BIT_0
+#define DFLG_RETRY_LOCAL_DEVICES BIT_1
+#define DFLG_FABRIC_DEVICES BIT_2
+#define SWITCH_FOUND BIT_3
+#define DFLG_NO_CABLE BIT_4
+
+ srb_t *status_srb; /* Status continuation entry. */
+
+ /* ISP configuration data. */
+ uint16_t loop_id; /* Host adapter loop id */
+
+ port_id_t d_id; /* Host adapter port id */
+ uint8_t marker_needed;
+ uint16_t mgmt_svr_loop_id;
+
+
+
+ /* RSCN queue. */
+ uint32_t rscn_queue[MAX_RSCN_COUNT];
+ uint8_t rscn_in_ptr;
+ uint8_t rscn_out_ptr;
+
+ /* Timeout timers. */
+ uint8_t loop_down_abort_time; /* port down timer */
+ atomic_t loop_down_timer; /* loop down timer */
+ uint8_t link_down_timeout; /* link down timeout */
+
+ uint32_t timer_active;
+ struct timer_list timer;
+
+ uint8_t node_name[WWN_SIZE];
+ uint8_t port_name[WWN_SIZE];
+ uint8_t fabric_node_name[WWN_SIZE];
+ uint32_t vp_abort_cnt;
- struct list_head vp_list; /* list of VP */
struct fc_vport *fc_vport; /* holds fc_vport * for each vport */
- unsigned long vp_idx_map[(MAX_MULTI_ID_FABRIC / 8) / sizeof(unsigned long)];
- uint16_t num_vhosts; /* number of vports created */
- uint16_t num_vsans; /* number of vsan created */
uint16_t vp_idx; /* vport ID */
- struct scsi_qla_host *parent; /* holds pport */
unsigned long vp_flags;
- struct list_head vp_fcports; /* list of fcports */
#define VP_IDX_ACQUIRED 0 /* bit no 0 */
#define VP_CREATE_NEEDED 1
#define VP_BIND_NEEDED 2
@@ -2604,14 +2684,10 @@ typedef struct scsi_qla_host {
#define VP_ERR_FAB_NORESOURCES 3
#define VP_ERR_FAB_LOGOUT 4
#define VP_ERR_ADAP_NORESOURCES 5
- uint16_t max_npiv_vports; /* 63 or 125 per topoloty */
- int cur_vport_count;
-
- struct qla_chip_state_84xx *cs84xx;
- struct qla_statistics qla_stats;
+ struct qla_hw_data *hw;
+ int req_ques[QLA_MAX_HOST_QUES];
} scsi_qla_host_t;
-
/*
* Macros to help code, maintain, etc.
*/
diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
index 561a4411719d..0e366a1b44b3 100644
--- a/drivers/scsi/qla2xxx/qla_dfs.c
+++ b/drivers/scsi/qla2xxx/qla_dfs.c
@@ -15,10 +15,11 @@ static atomic_t qla2x00_dfs_root_count;
static int
qla2x00_dfs_fce_show(struct seq_file *s, void *unused)
{
- scsi_qla_host_t *ha = s->private;
+ scsi_qla_host_t *vha = s->private;
uint32_t cnt;
uint32_t *fce;
uint64_t fce_start;
+ struct qla_hw_data *ha = vha->hw;
mutex_lock(&ha->fce_mutex);
@@ -51,7 +52,8 @@ qla2x00_dfs_fce_show(struct seq_file *s, void *unused)
static int
qla2x00_dfs_fce_open(struct inode *inode, struct file *file)
{
- scsi_qla_host_t *ha = inode->i_private;
+ scsi_qla_host_t *vha = inode->i_private;
+ struct qla_hw_data *ha = vha->hw;
int rval;
if (!ha->flags.fce_enabled)
@@ -60,7 +62,7 @@ qla2x00_dfs_fce_open(struct inode *inode, struct file *file)
mutex_lock(&ha->fce_mutex);
/* Pause tracing to flush FCE buffers. */
- rval = qla2x00_disable_fce_trace(ha, &ha->fce_wr, &ha->fce_rd);
+ rval = qla2x00_disable_fce_trace(vha, &ha->fce_wr, &ha->fce_rd);
if (rval)
qla_printk(KERN_WARNING, ha,
"DebugFS: Unable to disable FCE (%d).\n", rval);
@@ -75,7 +77,8 @@ out:
static int
qla2x00_dfs_fce_release(struct inode *inode, struct file *file)
{
- scsi_qla_host_t *ha = inode->i_private;
+ scsi_qla_host_t *vha = inode->i_private;
+ struct qla_hw_data *ha = vha->hw;
int rval;
if (ha->flags.fce_enabled)
@@ -86,7 +89,7 @@ qla2x00_dfs_fce_release(struct inode *inode, struct file *file)
/* Re-enable FCE tracing. */
ha->flags.fce_enabled = 1;
memset(ha->fce, 0, fce_calc_size(ha->fce_bufs));
- rval = qla2x00_enable_fce_trace(ha, ha->fce_dma, ha->fce_bufs,
+ rval = qla2x00_enable_fce_trace(vha, ha->fce_dma, ha->fce_bufs,
ha->fce_mb, &ha->fce_bufs);
if (rval) {
qla_printk(KERN_WARNING, ha,
@@ -107,8 +110,9 @@ static const struct file_operations dfs_fce_ops = {
};
int
-qla2x00_dfs_setup(scsi_qla_host_t *ha)
+qla2x00_dfs_setup(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
if (!IS_QLA25XX(ha))
goto out;
if (!ha->fce)
@@ -130,7 +134,7 @@ create_dir:
goto create_nodes;
mutex_init(&ha->fce_mutex);
- ha->dfs_dir = debugfs_create_dir(ha->host_str, qla2x00_dfs_root);
+ ha->dfs_dir = debugfs_create_dir(vha->host_str, qla2x00_dfs_root);
if (!ha->dfs_dir) {
qla_printk(KERN_NOTICE, ha,
"DebugFS: Unable to create ha directory.\n");
@@ -152,8 +156,9 @@ out:
}
int
-qla2x00_dfs_remove(scsi_qla_host_t *ha)
+qla2x00_dfs_remove(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
if (ha->dfs_fce) {
debugfs_remove(ha->dfs_fce);
ha->dfs_fce = NULL;
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index d1d14202575a..ee1f1e794c2d 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -299,7 +299,8 @@ struct init_cb_24xx {
uint32_t response_q_address[2];
uint32_t prio_request_q_address[2];
- uint8_t reserved_2[8];
+ uint16_t msix;
+ uint8_t reserved_2[6];
uint16_t atio_q_inpointer;
uint16_t atio_q_length;
@@ -372,8 +373,9 @@ struct init_cb_24xx {
* BIT 17-31 = Reserved
*/
uint32_t firmware_options_3;
-
- uint8_t reserved_3[24];
+ uint16_t qos;
+ uint16_t rid;
+ uint8_t reserved_3[20];
};
/*
@@ -754,7 +756,8 @@ struct abort_entry_24xx {
uint32_t handle_to_abort; /* System handle to abort. */
- uint8_t reserved_1[32];
+ uint16_t req_que_no;
+ uint8_t reserved_1[30];
uint8_t port_id[3]; /* PortID of destination port. */
uint8_t vp_index;
@@ -1258,7 +1261,8 @@ struct qla_npiv_header {
struct qla_npiv_entry {
uint16_t flags;
uint16_t vf_id;
- uint16_t qos;
+ uint8_t q_qos;
+ uint8_t f_qos;
uint16_t unused1;
uint8_t port_name[WWN_SIZE];
uint8_t node_name[WWN_SIZE];
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 753dbe6cce6e..0011e31205db 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -63,6 +63,7 @@ extern int ql2xallocfwdump;
extern int ql2xextended_error_logging;
extern int ql2xqfullrampup;
extern int ql2xiidmaenable;
+extern int ql2xmaxqueues;
extern int qla2x00_loop_reset(scsi_qla_host_t *);
extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
@@ -72,7 +73,10 @@ extern int qla2x00_post_hwe_work(struct scsi_qla_host *, uint16_t , uint16_t,
uint16_t, uint16_t);
extern void qla2x00_abort_fcport_cmds(fc_port_t *);
-
+extern struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *,
+ struct qla_hw_data *);
+extern void qla2x00_free_host(struct scsi_qla_host *);
+extern void qla2x00_relogin(struct scsi_qla_host *);
/*
* Global Functions in qla_mid.c source file.
*/
@@ -94,7 +98,7 @@ extern void qla2x00_do_dpc_all_vps(scsi_qla_host_t *);
extern int qla24xx_vport_create_req_sanity_check(struct fc_vport *);
extern scsi_qla_host_t * qla24xx_create_vhost(struct fc_vport *);
-extern void qla2x00_sp_compl(scsi_qla_host_t *, srb_t *);
+extern void qla2x00_sp_compl(struct qla_hw_data *, srb_t *);
extern char *qla2x00_get_fw_version_str(struct scsi_qla_host *, char *);
@@ -105,10 +109,11 @@ extern struct fw_blob *qla2x00_request_firmware(scsi_qla_host_t *);
extern int qla2x00_wait_for_hba_online(scsi_qla_host_t *);
-extern void qla2xxx_wake_dpc(scsi_qla_host_t *);
-extern void qla2x00_alert_all_vps(scsi_qla_host_t *, uint16_t *);
-extern void qla2x00_async_event(scsi_qla_host_t *, uint16_t *);
-extern void qla2x00_vp_abort_isp(scsi_qla_host_t *);
+extern void qla2xxx_wake_dpc(struct scsi_qla_host *);
+extern void qla2x00_alert_all_vps(struct rsp_que *, uint16_t *);
+extern void qla2x00_async_event(scsi_qla_host_t *, struct rsp_que *,
+ uint16_t *);
+extern int qla2x00_vp_abort_isp(scsi_qla_host_t *);
/*
* Global Function Prototypes in qla_iocb.c source file.
@@ -119,8 +124,10 @@ extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t);
extern void qla2x00_build_scsi_iocbs_64(srb_t *, cmd_entry_t *, uint16_t);
extern int qla2x00_start_scsi(srb_t *sp);
extern int qla24xx_start_scsi(srb_t *sp);
-int qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t);
-int __qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t);
+int qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *,
+ uint16_t, uint16_t, uint8_t);
+int __qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *,
+ uint16_t, uint16_t, uint8_t);
/*
* Global Function Prototypes in qla_mbx.c source file.
@@ -154,7 +161,7 @@ extern int
qla2x00_issue_iocb(scsi_qla_host_t *, void *, dma_addr_t, size_t);
extern int
-qla2x00_abort_command(scsi_qla_host_t *, srb_t *);
+qla2x00_abort_command(scsi_qla_host_t *, srb_t *, struct req_que *);
extern int
qla2x00_abort_target(struct fc_port *, unsigned int);
@@ -225,7 +232,7 @@ extern int
qla24xx_get_isp_stats(scsi_qla_host_t *, struct link_statistics *,
dma_addr_t);
-extern int qla24xx_abort_command(scsi_qla_host_t *, srb_t *);
+extern int qla24xx_abort_command(scsi_qla_host_t *, srb_t *, struct req_que *);
extern int qla24xx_abort_target(struct fc_port *, unsigned int);
extern int qla24xx_lun_reset(struct fc_port *, unsigned int);
@@ -264,10 +271,10 @@ extern int qla84xx_verify_chip(struct scsi_qla_host *, uint16_t *);
extern irqreturn_t qla2100_intr_handler(int, void *);
extern irqreturn_t qla2300_intr_handler(int, void *);
extern irqreturn_t qla24xx_intr_handler(int, void *);
-extern void qla2x00_process_response_queue(struct scsi_qla_host *);
-extern void qla24xx_process_response_queue(struct scsi_qla_host *);
+extern void qla2x00_process_response_queue(struct rsp_que *);
+extern void qla24xx_process_response_queue(struct rsp_que *);
-extern int qla2x00_request_irqs(scsi_qla_host_t *);
+extern int qla2x00_request_irqs(struct qla_hw_data *, struct rsp_que *);
extern void qla2x00_free_irqs(scsi_qla_host_t *);
/*
@@ -367,4 +374,27 @@ extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *);
*/
extern int qla2x00_dfs_setup(scsi_qla_host_t *);
extern int qla2x00_dfs_remove(scsi_qla_host_t *);
+
+/* Globa function prototypes for multi-q */
+extern int qla25xx_request_irq(struct rsp_que *);
+extern int qla25xx_init_req_que(struct scsi_qla_host *, struct req_que *,
+ uint8_t);
+extern int qla25xx_init_rsp_que(struct scsi_qla_host *, struct rsp_que *,
+ uint8_t);
+extern int qla25xx_create_req_que(struct qla_hw_data *, uint16_t, uint8_t,
+ uint16_t, uint8_t, uint8_t);
+extern int qla25xx_create_rsp_que(struct qla_hw_data *, uint16_t, uint8_t,
+ uint16_t);
+extern int qla25xx_update_req_que(struct scsi_qla_host *, uint8_t, uint8_t);
+extern void qla2x00_init_response_q_entries(struct rsp_que *);
+extern int qla25xx_delete_req_que(struct scsi_qla_host *, struct req_que *);
+extern int qla25xx_delete_rsp_que(struct scsi_qla_host *, struct rsp_que *);
+extern int qla25xx_create_queues(struct scsi_qla_host *, uint8_t);
+extern int qla25xx_delete_queues(struct scsi_qla_host *, uint8_t);
+extern uint16_t qla24xx_rd_req_reg(struct qla_hw_data *, uint16_t);
+extern uint16_t qla25xx_rd_req_reg(struct qla_hw_data *, uint16_t);
+extern void qla24xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t);
+extern void qla25xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t);
+extern void qla25xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t);
+extern void qla24xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t);
#endif /* _QLA_GBL_H */
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index c2a4bfbcb05b..0a6f72973996 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -22,8 +22,9 @@ static int qla2x00_sns_rnn_id(scsi_qla_host_t *);
* Returns a pointer to the @ha's ms_iocb.
*/
void *
-qla2x00_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size)
+qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size)
{
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
ms_pkt = ha->ms_iocb;
@@ -59,8 +60,9 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size)
* Returns a pointer to the @ha's ms_iocb.
*/
void *
-qla24xx_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size)
+qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size)
{
+ struct qla_hw_data *ha = vha->hw;
struct ct_entry_24xx *ct_pkt;
ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
@@ -82,7 +84,7 @@ qla24xx_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size)
ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
- ct_pkt->vp_index = ha->vp_idx;
+ ct_pkt->vp_index = vha->vp_idx;
return (ct_pkt);
}
@@ -110,16 +112,17 @@ qla2x00_prep_ct_req(struct ct_sns_req *ct_req, uint16_t cmd, uint16_t rsp_size)
}
static int
-qla2x00_chk_ms_status(scsi_qla_host_t *ha, ms_iocb_entry_t *ms_pkt,
+qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
struct ct_sns_rsp *ct_rsp, const char *routine)
{
int rval;
uint16_t comp_status;
+ struct qla_hw_data *ha = vha->hw;
rval = QLA_FUNCTION_FAILED;
if (ms_pkt->entry_status != 0) {
DEBUG2_3(printk("scsi(%ld): %s failed, error status (%x).\n",
- ha->host_no, routine, ms_pkt->entry_status));
+ vha->host_no, routine, ms_pkt->entry_status));
} else {
if (IS_FWI2_CAPABLE(ha))
comp_status = le16_to_cpu(
@@ -133,7 +136,7 @@ qla2x00_chk_ms_status(scsi_qla_host_t *ha, ms_iocb_entry_t *ms_pkt,
if (ct_rsp->header.response !=
__constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
DEBUG2_3(printk("scsi(%ld): %s failed, "
- "rejected request:\n", ha->host_no,
+ "rejected request:\n", vha->host_no,
routine));
DEBUG2_3(qla2x00_dump_buffer(
(uint8_t *)&ct_rsp->header,
@@ -144,7 +147,7 @@ qla2x00_chk_ms_status(scsi_qla_host_t *ha, ms_iocb_entry_t *ms_pkt,
break;
default:
DEBUG2_3(printk("scsi(%ld): %s failed, completion "
- "status (%x).\n", ha->host_no, routine,
+ "status (%x).\n", vha->host_no, routine,
comp_status));
break;
}
@@ -160,21 +163,21 @@ qla2x00_chk_ms_status(scsi_qla_host_t *ha, ms_iocb_entry_t *ms_pkt,
* Returns 0 on success.
*/
int
-qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
+qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport)
{
int rval;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
+ struct qla_hw_data *ha = vha->hw;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- return (qla2x00_sns_ga_nxt(ha, fcport));
- }
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_ga_nxt(vha, fcport);
/* Issue GA_NXT */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GA_NXT_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GA_NXT_REQ_SIZE,
GA_NXT_RSP_SIZE);
/* Prepare CT request */
@@ -188,13 +191,13 @@ qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
ct_req->req.port_id.port_id[2] = fcport->d_id.b.al_pa;
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GA_NXT issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "GA_NXT") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GA_NXT") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
@@ -216,7 +219,7 @@ qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
"nn %02x%02x%02x%02x%02x%02x%02x%02x "
"pn %02x%02x%02x%02x%02x%02x%02x%02x "
"portid=%02x%02x%02x.\n",
- ha->host_no,
+ vha->host_no,
fcport->node_name[0], fcport->node_name[1],
fcport->node_name[2], fcport->node_name[3],
fcport->node_name[4], fcport->node_name[5],
@@ -242,7 +245,7 @@ qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
* Returns 0 on success.
*/
int
-qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
uint16_t i;
@@ -252,16 +255,16 @@ qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
struct ct_sns_rsp *ct_rsp;
struct ct_sns_gid_pt_data *gid_data;
+ struct qla_hw_data *ha = vha->hw;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- return (qla2x00_sns_gid_pt(ha, list));
- }
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_gid_pt(vha, list);
gid_data = NULL;
/* Issue GID_PT */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GID_PT_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GID_PT_REQ_SIZE,
GID_PT_RSP_SIZE);
/* Prepare CT request */
@@ -273,13 +276,13 @@ qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
ct_req->req.gid_pt.port_type = NS_NX_PORT_TYPE;
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GID_PT issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "GID_PT") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GID_PT") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
@@ -320,7 +323,7 @@ qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
* Returns 0 on success.
*/
int
-qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
uint16_t i;
@@ -328,15 +331,15 @@ qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
+ struct qla_hw_data *ha = vha->hw;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- return (qla2x00_sns_gpn_id(ha, list));
- }
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_gpn_id(vha, list);
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GPN_ID */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GPN_ID_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GPN_ID_REQ_SIZE,
GPN_ID_RSP_SIZE);
/* Prepare CT request */
@@ -350,13 +353,13 @@ qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GPN_ID issue IOCB failed "
- "(%d).\n", ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp,
+ "(%d).\n", vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
"GPN_ID") != QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
@@ -381,23 +384,22 @@ qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
* Returns 0 on success.
*/
int
-qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
uint16_t i;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- return (qla2x00_sns_gnn_id(ha, list));
- }
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_gnn_id(vha, list);
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GNN_ID */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GNN_ID_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GNN_ID_REQ_SIZE,
GNN_ID_RSP_SIZE);
/* Prepare CT request */
@@ -411,13 +413,13 @@ qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GNN_ID issue IOCB failed "
- "(%d).\n", ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp,
+ "(%d).\n", vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
"GNN_ID") != QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
@@ -429,7 +431,7 @@ qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
"nn %02x%02x%02x%02x%02x%02x%02x%02x "
"pn %02x%02x%02x%02x%02x%02x%02x%02x "
"portid=%02x%02x%02x.\n",
- ha->host_no,
+ vha->host_no,
list[i].node_name[0], list[i].node_name[1],
list[i].node_name[2], list[i].node_name[3],
list[i].node_name[4], list[i].node_name[5],
@@ -457,21 +459,20 @@ qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
* Returns 0 on success.
*/
int
-qla2x00_rft_id(scsi_qla_host_t *ha)
+qla2x00_rft_id(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- return (qla2x00_sns_rft_id(ha));
- }
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_rft_id(vha);
/* Issue RFT_ID */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, RFT_ID_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RFT_ID_REQ_SIZE,
RFT_ID_RSP_SIZE);
/* Prepare CT request */
@@ -480,25 +481,25 @@ qla2x00_rft_id(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id, FC-4 types */
- ct_req->req.rft_id.port_id[0] = ha->d_id.b.domain;
- ct_req->req.rft_id.port_id[1] = ha->d_id.b.area;
- ct_req->req.rft_id.port_id[2] = ha->d_id.b.al_pa;
+ ct_req->req.rft_id.port_id[0] = vha->d_id.b.domain;
+ ct_req->req.rft_id.port_id[1] = vha->d_id.b.area;
+ ct_req->req.rft_id.port_id[2] = vha->d_id.b.al_pa;
ct_req->req.rft_id.fc4_types[2] = 0x01; /* FCP-3 */
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RFT_ID issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RFT_ID") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RFT_ID") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -511,23 +512,23 @@ qla2x00_rft_id(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2x00_rff_id(scsi_qla_host_t *ha)
+qla2x00_rff_id(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
DEBUG2(printk("scsi(%ld): RFF_ID call unsupported on "
- "ISP2100/ISP2200.\n", ha->host_no));
+ "ISP2100/ISP2200.\n", vha->host_no));
return (QLA_SUCCESS);
}
/* Issue RFF_ID */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, RFF_ID_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RFF_ID_REQ_SIZE,
RFF_ID_RSP_SIZE);
/* Prepare CT request */
@@ -536,26 +537,26 @@ qla2x00_rff_id(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id, FC-4 feature, FC-4 type */
- ct_req->req.rff_id.port_id[0] = ha->d_id.b.domain;
- ct_req->req.rff_id.port_id[1] = ha->d_id.b.area;
- ct_req->req.rff_id.port_id[2] = ha->d_id.b.al_pa;
+ ct_req->req.rff_id.port_id[0] = vha->d_id.b.domain;
+ ct_req->req.rff_id.port_id[1] = vha->d_id.b.area;
+ ct_req->req.rff_id.port_id[2] = vha->d_id.b.al_pa;
ct_req->req.rff_id.fc4_feature = BIT_1;
ct_req->req.rff_id.fc4_type = 0x08; /* SCSI - FCP */
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RFF_ID issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RFF_ID") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RFF_ID") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RFF_ID exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -568,21 +569,20 @@ qla2x00_rff_id(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2x00_rnn_id(scsi_qla_host_t *ha)
+qla2x00_rnn_id(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- return (qla2x00_sns_rnn_id(ha));
- }
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_rnn_id(vha);
/* Issue RNN_ID */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, RNN_ID_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RNN_ID_REQ_SIZE,
RNN_ID_RSP_SIZE);
/* Prepare CT request */
@@ -591,33 +591,34 @@ qla2x00_rnn_id(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id, node_name */
- ct_req->req.rnn_id.port_id[0] = ha->d_id.b.domain;
- ct_req->req.rnn_id.port_id[1] = ha->d_id.b.area;
- ct_req->req.rnn_id.port_id[2] = ha->d_id.b.al_pa;
+ ct_req->req.rnn_id.port_id[0] = vha->d_id.b.domain;
+ ct_req->req.rnn_id.port_id[1] = vha->d_id.b.area;
+ ct_req->req.rnn_id.port_id[2] = vha->d_id.b.al_pa;
- memcpy(ct_req->req.rnn_id.node_name, ha->node_name, WWN_SIZE);
+ memcpy(ct_req->req.rnn_id.node_name, vha->node_name, WWN_SIZE);
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RNN_ID issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RNN_ID") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RNN_ID") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
}
void
-qla2x00_get_sym_node_name(scsi_qla_host_t *ha, uint8_t *snn)
+qla2x00_get_sym_node_name(scsi_qla_host_t *vha, uint8_t *snn)
{
+ struct qla_hw_data *ha = vha->hw;
sprintf(snn, "%s FW:v%d.%02d.%02d DVR:v%s",ha->model_number,
ha->fw_major_version, ha->fw_minor_version,
ha->fw_subminor_version, qla2x00_version_str);
@@ -630,23 +631,24 @@ qla2x00_get_sym_node_name(scsi_qla_host_t *ha, uint8_t *snn)
* Returns 0 on success.
*/
int
-qla2x00_rsnn_nn(scsi_qla_host_t *ha)
+qla2x00_rsnn_nn(scsi_qla_host_t *vha)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
DEBUG2(printk("scsi(%ld): RSNN_ID call unsupported on "
- "ISP2100/ISP2200.\n", ha->host_no));
+ "ISP2100/ISP2200.\n", vha->host_no));
return (QLA_SUCCESS);
}
/* Issue RSNN_NN */
/* Prepare common MS IOCB */
/* Request size adjusted after CT preparation */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, 0, RSNN_NN_RSP_SIZE);
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, 0, RSNN_NN_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RSNN_NN_CMD,
@@ -654,10 +656,10 @@ qla2x00_rsnn_nn(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- node_name, symbolic node_name, size */
- memcpy(ct_req->req.rsnn_nn.node_name, ha->node_name, WWN_SIZE);
+ memcpy(ct_req->req.rsnn_nn.node_name, vha->node_name, WWN_SIZE);
/* Prepare the Symbolic Node Name */
- qla2x00_get_sym_node_name(ha, ct_req->req.rsnn_nn.sym_node_name);
+ qla2x00_get_sym_node_name(vha, ct_req->req.rsnn_nn.sym_node_name);
/* Calculate SNN length */
ct_req->req.rsnn_nn.name_len =
@@ -669,18 +671,18 @@ qla2x00_rsnn_nn(scsi_qla_host_t *ha)
ms_pkt->dseg_req_length = ms_pkt->req_bytecount;
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RSNN_NN issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RSNN_NN") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RSNN_NN") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RSNN_NN exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -696,11 +698,12 @@ qla2x00_rsnn_nn(scsi_qla_host_t *ha)
* Returns a pointer to the @ha's sns_cmd.
*/
static inline struct sns_cmd_pkt *
-qla2x00_prep_sns_cmd(scsi_qla_host_t *ha, uint16_t cmd, uint16_t scmd_len,
+qla2x00_prep_sns_cmd(scsi_qla_host_t *vha, uint16_t cmd, uint16_t scmd_len,
uint16_t data_size)
{
uint16_t wc;
struct sns_cmd_pkt *sns_cmd;
+ struct qla_hw_data *ha = vha->hw;
sns_cmd = ha->sns_cmd;
memset(sns_cmd, 0, sizeof(struct sns_cmd_pkt));
@@ -726,15 +729,15 @@ qla2x00_prep_sns_cmd(scsi_qla_host_t *ha, uint16_t cmd, uint16_t scmd_len,
* Returns 0 on success.
*/
static int
-qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
+qla2x00_sns_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
struct sns_cmd_pkt *sns_cmd;
/* Issue GA_NXT. */
/* Prepare SNS command request. */
- sns_cmd = qla2x00_prep_sns_cmd(ha, GA_NXT_CMD, GA_NXT_SNS_SCMD_LEN,
+ sns_cmd = qla2x00_prep_sns_cmd(vha, GA_NXT_CMD, GA_NXT_SNS_SCMD_LEN,
GA_NXT_SNS_DATA_SIZE);
/* Prepare SNS command arguments -- port_id. */
@@ -743,16 +746,16 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
sns_cmd->p.cmd.param[2] = fcport->d_id.b.domain;
/* Execute SNS command. */
- rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, GA_NXT_SNS_CMD_SIZE / 2,
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, GA_NXT_SNS_CMD_SIZE / 2,
sizeof(struct sns_cmd_pkt));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GA_NXT Send SNS failed (%d).\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else if (sns_cmd->p.gan_data[8] != 0x80 ||
sns_cmd->p.gan_data[9] != 0x02) {
DEBUG2_3(printk("scsi(%ld): GA_NXT failed, rejected request, "
- "ga_nxt_rsp:\n", ha->host_no));
+ "ga_nxt_rsp:\n", vha->host_no));
DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gan_data, 16));
rval = QLA_FUNCTION_FAILED;
} else {
@@ -772,7 +775,7 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
"nn %02x%02x%02x%02x%02x%02x%02x%02x "
"pn %02x%02x%02x%02x%02x%02x%02x%02x "
"portid=%02x%02x%02x.\n",
- ha->host_no,
+ vha->host_no,
fcport->node_name[0], fcport->node_name[1],
fcport->node_name[2], fcport->node_name[3],
fcport->node_name[4], fcport->node_name[5],
@@ -800,33 +803,33 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
* Returns 0 on success.
*/
static int
-qla2x00_sns_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
uint16_t i;
uint8_t *entry;
struct sns_cmd_pkt *sns_cmd;
/* Issue GID_PT. */
/* Prepare SNS command request. */
- sns_cmd = qla2x00_prep_sns_cmd(ha, GID_PT_CMD, GID_PT_SNS_SCMD_LEN,
+ sns_cmd = qla2x00_prep_sns_cmd(vha, GID_PT_CMD, GID_PT_SNS_SCMD_LEN,
GID_PT_SNS_DATA_SIZE);
/* Prepare SNS command arguments -- port_type. */
sns_cmd->p.cmd.param[0] = NS_NX_PORT_TYPE;
/* Execute SNS command. */
- rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, GID_PT_SNS_CMD_SIZE / 2,
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, GID_PT_SNS_CMD_SIZE / 2,
sizeof(struct sns_cmd_pkt));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GID_PT Send SNS failed (%d).\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else if (sns_cmd->p.gid_data[8] != 0x80 ||
sns_cmd->p.gid_data[9] != 0x02) {
DEBUG2_3(printk("scsi(%ld): GID_PT failed, rejected request, "
- "gid_rsp:\n", ha->host_no));
+ "gid_rsp:\n", vha->host_no));
DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gid_data, 16));
rval = QLA_FUNCTION_FAILED;
} else {
@@ -867,17 +870,17 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
* Returns 0 on success.
*/
static int
-qla2x00_sns_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_sns_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
uint16_t i;
struct sns_cmd_pkt *sns_cmd;
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GPN_ID */
/* Prepare SNS command request. */
- sns_cmd = qla2x00_prep_sns_cmd(ha, GPN_ID_CMD,
+ sns_cmd = qla2x00_prep_sns_cmd(vha, GPN_ID_CMD,
GPN_ID_SNS_SCMD_LEN, GPN_ID_SNS_DATA_SIZE);
/* Prepare SNS command arguments -- port_id. */
@@ -886,16 +889,16 @@ qla2x00_sns_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
sns_cmd->p.cmd.param[2] = list[i].d_id.b.domain;
/* Execute SNS command. */
- rval = qla2x00_send_sns(ha, ha->sns_cmd_dma,
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma,
GPN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GPN_ID Send SNS failed "
- "(%d).\n", ha->host_no, rval));
+ "(%d).\n", vha->host_no, rval));
} else if (sns_cmd->p.gpn_data[8] != 0x80 ||
sns_cmd->p.gpn_data[9] != 0x02) {
DEBUG2_3(printk("scsi(%ld): GPN_ID failed, rejected "
- "request, gpn_rsp:\n", ha->host_no));
+ "request, gpn_rsp:\n", vha->host_no));
DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gpn_data, 16));
rval = QLA_FUNCTION_FAILED;
} else {
@@ -922,17 +925,17 @@ qla2x00_sns_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
* Returns 0 on success.
*/
static int
-qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_sns_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
uint16_t i;
struct sns_cmd_pkt *sns_cmd;
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GNN_ID */
/* Prepare SNS command request. */
- sns_cmd = qla2x00_prep_sns_cmd(ha, GNN_ID_CMD,
+ sns_cmd = qla2x00_prep_sns_cmd(vha, GNN_ID_CMD,
GNN_ID_SNS_SCMD_LEN, GNN_ID_SNS_DATA_SIZE);
/* Prepare SNS command arguments -- port_id. */
@@ -941,16 +944,16 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
sns_cmd->p.cmd.param[2] = list[i].d_id.b.domain;
/* Execute SNS command. */
- rval = qla2x00_send_sns(ha, ha->sns_cmd_dma,
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma,
GNN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GNN_ID Send SNS failed "
- "(%d).\n", ha->host_no, rval));
+ "(%d).\n", vha->host_no, rval));
} else if (sns_cmd->p.gnn_data[8] != 0x80 ||
sns_cmd->p.gnn_data[9] != 0x02) {
DEBUG2_3(printk("scsi(%ld): GNN_ID failed, rejected "
- "request, gnn_rsp:\n", ha->host_no));
+ "request, gnn_rsp:\n", vha->host_no));
DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gnn_data, 16));
rval = QLA_FUNCTION_FAILED;
} else {
@@ -962,7 +965,7 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
"nn %02x%02x%02x%02x%02x%02x%02x%02x "
"pn %02x%02x%02x%02x%02x%02x%02x%02x "
"portid=%02x%02x%02x.\n",
- ha->host_no,
+ vha->host_no,
list[i].node_name[0], list[i].node_name[1],
list[i].node_name[2], list[i].node_name[3],
list[i].node_name[4], list[i].node_name[5],
@@ -992,40 +995,40 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
* Returns 0 on success.
*/
static int
-qla2x00_sns_rft_id(scsi_qla_host_t *ha)
+qla2x00_sns_rft_id(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
struct sns_cmd_pkt *sns_cmd;
/* Issue RFT_ID. */
/* Prepare SNS command request. */
- sns_cmd = qla2x00_prep_sns_cmd(ha, RFT_ID_CMD, RFT_ID_SNS_SCMD_LEN,
+ sns_cmd = qla2x00_prep_sns_cmd(vha, RFT_ID_CMD, RFT_ID_SNS_SCMD_LEN,
RFT_ID_SNS_DATA_SIZE);
/* Prepare SNS command arguments -- port_id, FC-4 types */
- sns_cmd->p.cmd.param[0] = ha->d_id.b.al_pa;
- sns_cmd->p.cmd.param[1] = ha->d_id.b.area;
- sns_cmd->p.cmd.param[2] = ha->d_id.b.domain;
+ sns_cmd->p.cmd.param[0] = vha->d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = vha->d_id.b.area;
+ sns_cmd->p.cmd.param[2] = vha->d_id.b.domain;
sns_cmd->p.cmd.param[5] = 0x01; /* FCP-3 */
/* Execute SNS command. */
- rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, RFT_ID_SNS_CMD_SIZE / 2,
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, RFT_ID_SNS_CMD_SIZE / 2,
sizeof(struct sns_cmd_pkt));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RFT_ID Send SNS failed (%d).\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else if (sns_cmd->p.rft_data[8] != 0x80 ||
sns_cmd->p.rft_data[9] != 0x02) {
DEBUG2_3(printk("scsi(%ld): RFT_ID failed, rejected request, "
- "rft_rsp:\n", ha->host_no));
+ "rft_rsp:\n", vha->host_no));
DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.rft_data, 16));
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -1041,47 +1044,47 @@ qla2x00_sns_rft_id(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_sns_rnn_id(scsi_qla_host_t *ha)
+qla2x00_sns_rnn_id(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
struct sns_cmd_pkt *sns_cmd;
/* Issue RNN_ID. */
/* Prepare SNS command request. */
- sns_cmd = qla2x00_prep_sns_cmd(ha, RNN_ID_CMD, RNN_ID_SNS_SCMD_LEN,
+ sns_cmd = qla2x00_prep_sns_cmd(vha, RNN_ID_CMD, RNN_ID_SNS_SCMD_LEN,
RNN_ID_SNS_DATA_SIZE);
/* Prepare SNS command arguments -- port_id, nodename. */
- sns_cmd->p.cmd.param[0] = ha->d_id.b.al_pa;
- sns_cmd->p.cmd.param[1] = ha->d_id.b.area;
- sns_cmd->p.cmd.param[2] = ha->d_id.b.domain;
-
- sns_cmd->p.cmd.param[4] = ha->node_name[7];
- sns_cmd->p.cmd.param[5] = ha->node_name[6];
- sns_cmd->p.cmd.param[6] = ha->node_name[5];
- sns_cmd->p.cmd.param[7] = ha->node_name[4];
- sns_cmd->p.cmd.param[8] = ha->node_name[3];
- sns_cmd->p.cmd.param[9] = ha->node_name[2];
- sns_cmd->p.cmd.param[10] = ha->node_name[1];
- sns_cmd->p.cmd.param[11] = ha->node_name[0];
+ sns_cmd->p.cmd.param[0] = vha->d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = vha->d_id.b.area;
+ sns_cmd->p.cmd.param[2] = vha->d_id.b.domain;
+
+ sns_cmd->p.cmd.param[4] = vha->node_name[7];
+ sns_cmd->p.cmd.param[5] = vha->node_name[6];
+ sns_cmd->p.cmd.param[6] = vha->node_name[5];
+ sns_cmd->p.cmd.param[7] = vha->node_name[4];
+ sns_cmd->p.cmd.param[8] = vha->node_name[3];
+ sns_cmd->p.cmd.param[9] = vha->node_name[2];
+ sns_cmd->p.cmd.param[10] = vha->node_name[1];
+ sns_cmd->p.cmd.param[11] = vha->node_name[0];
/* Execute SNS command. */
- rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, RNN_ID_SNS_CMD_SIZE / 2,
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, RNN_ID_SNS_CMD_SIZE / 2,
sizeof(struct sns_cmd_pkt));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RNN_ID Send SNS failed (%d).\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else if (sns_cmd->p.rnn_data[8] != 0x80 ||
sns_cmd->p.rnn_data[9] != 0x02) {
DEBUG2_3(printk("scsi(%ld): RNN_ID failed, rejected request, "
- "rnn_rsp:\n", ha->host_no));
+ "rnn_rsp:\n", vha->host_no));
DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.rnn_data, 16));
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -1094,25 +1097,25 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_mgmt_svr_login(scsi_qla_host_t *ha)
+qla2x00_mgmt_svr_login(scsi_qla_host_t *vha)
{
int ret;
uint16_t mb[MAILBOX_REGISTER_COUNT];
-
+ struct qla_hw_data *ha = vha->hw;
ret = QLA_SUCCESS;
- if (ha->flags.management_server_logged_in)
+ if (vha->flags.management_server_logged_in)
return ret;
- ha->isp_ops->fabric_login(ha, ha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa,
+ ha->isp_ops->fabric_login(vha, vha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa,
mb, BIT_1);
if (mb[0] != MBS_COMMAND_COMPLETE) {
DEBUG2_13(printk("%s(%ld): Failed MANAGEMENT_SERVER login: "
"loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x\n",
- __func__, ha->host_no, ha->mgmt_svr_loop_id, mb[0], mb[1],
+ __func__, vha->host_no, vha->mgmt_svr_loop_id, mb[0], mb[1],
mb[2], mb[6], mb[7]));
ret = QLA_FUNCTION_FAILED;
} else
- ha->flags.management_server_logged_in = 1;
+ vha->flags.management_server_logged_in = 1;
return ret;
}
@@ -1126,17 +1129,17 @@ qla2x00_mgmt_svr_login(scsi_qla_host_t *ha)
* Returns a pointer to the @ha's ms_iocb.
*/
void *
-qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size,
+qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size,
uint32_t rsp_size)
{
ms_iocb_entry_t *ms_pkt;
-
+ struct qla_hw_data *ha = vha->hw;
ms_pkt = ha->ms_iocb;
memset(ms_pkt, 0, sizeof(ms_iocb_entry_t));
ms_pkt->entry_type = MS_IOCB_TYPE;
ms_pkt->entry_count = 1;
- SET_TARGET_ID(ha, ms_pkt->loop_id, ha->mgmt_svr_loop_id);
+ SET_TARGET_ID(ha, ms_pkt->loop_id, vha->mgmt_svr_loop_id);
ms_pkt->control_flags = __constant_cpu_to_le16(CF_READ | CF_HEAD_TAG);
ms_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
ms_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
@@ -1164,17 +1167,18 @@ qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size,
* Returns a pointer to the @ha's ms_iocb.
*/
void *
-qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size,
+qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size,
uint32_t rsp_size)
{
struct ct_entry_24xx *ct_pkt;
+ struct qla_hw_data *ha = vha->hw;
ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
ct_pkt->entry_type = CT_IOCB_TYPE;
ct_pkt->entry_count = 1;
- ct_pkt->nport_handle = cpu_to_le16(ha->mgmt_svr_loop_id);
+ ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id);
ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1);
@@ -1188,14 +1192,15 @@ qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size,
ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
- ct_pkt->vp_index = ha->vp_idx;
+ ct_pkt->vp_index = vha->vp_idx;
return ct_pkt;
}
static inline ms_iocb_entry_t *
-qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size)
+qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size)
{
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt = ha->ms_iocb;
struct ct_entry_24xx *ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
@@ -1240,7 +1245,7 @@ qla2x00_prep_ct_fdmi_req(struct ct_sns_req *ct_req, uint16_t cmd,
* Returns 0 on success.
*/
static int
-qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
+qla2x00_fdmi_rhba(scsi_qla_host_t *vha)
{
int rval, alen;
uint32_t size, sn;
@@ -1250,11 +1255,12 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
struct ct_sns_rsp *ct_rsp;
uint8_t *entries;
struct ct_fdmi_hba_attr *eiter;
+ struct qla_hw_data *ha = vha->hw;
/* Issue RHBA */
/* Prepare common MS IOCB */
/* Request size adjusted after CT preparation */
- ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(ha, 0, RHBA_RSP_SIZE);
+ ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, 0, RHBA_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RHBA_CMD,
@@ -1262,9 +1268,9 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare FDMI command arguments -- attribute block, attributes. */
- memcpy(ct_req->req.rhba.hba_identifier, ha->port_name, WWN_SIZE);
+ memcpy(ct_req->req.rhba.hba_identifier, vha->port_name, WWN_SIZE);
ct_req->req.rhba.entry_count = __constant_cpu_to_be32(1);
- memcpy(ct_req->req.rhba.port_name, ha->port_name, WWN_SIZE);
+ memcpy(ct_req->req.rhba.port_name, vha->port_name, WWN_SIZE);
size = 2 * WWN_SIZE + 4 + 4;
/* Attributes */
@@ -1276,11 +1282,11 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter = (struct ct_fdmi_hba_attr *) (entries + size);
eiter->type = __constant_cpu_to_be16(FDMI_HBA_NODE_NAME);
eiter->len = __constant_cpu_to_be16(4 + WWN_SIZE);
- memcpy(eiter->a.node_name, ha->node_name, WWN_SIZE);
+ memcpy(eiter->a.node_name, vha->node_name, WWN_SIZE);
size += 4 + WWN_SIZE;
DEBUG13(printk("%s(%ld): NODENAME=%02x%02x%02x%02x%02x%02x%02x%02x.\n",
- __func__, ha->host_no,
+ __func__, vha->host_no,
eiter->a.node_name[0], eiter->a.node_name[1], eiter->a.node_name[2],
eiter->a.node_name[3], eiter->a.node_name[4], eiter->a.node_name[5],
eiter->a.node_name[6], eiter->a.node_name[7]));
@@ -1294,7 +1300,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): MANUFACTURER=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): MANUFACTURER=%s.\n", __func__, vha->host_no,
eiter->a.manufacturer));
/* Serial number. */
@@ -1307,7 +1313,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): SERIALNO=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): SERIALNO=%s.\n", __func__, vha->host_no,
eiter->a.serial_num));
/* Model name. */
@@ -1319,7 +1325,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): MODEL_NAME=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): MODEL_NAME=%s.\n", __func__, vha->host_no,
eiter->a.model));
/* Model description. */
@@ -1332,7 +1338,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): MODEL_DESC=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): MODEL_DESC=%s.\n", __func__, vha->host_no,
eiter->a.model_desc));
/* Hardware version. */
@@ -1344,7 +1350,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): HARDWAREVER=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): HARDWAREVER=%s.\n", __func__, vha->host_no,
eiter->a.hw_version));
/* Driver version. */
@@ -1356,7 +1362,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): DRIVERVER=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): DRIVERVER=%s.\n", __func__, vha->host_no,
eiter->a.driver_version));
/* Option ROM version. */
@@ -1368,27 +1374,27 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): OPTROMVER=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): OPTROMVER=%s.\n", __func__, vha->host_no,
eiter->a.orom_version));
/* Firmware version */
eiter = (struct ct_fdmi_hba_attr *) (entries + size);
eiter->type = __constant_cpu_to_be16(FDMI_HBA_FIRMWARE_VERSION);
- ha->isp_ops->fw_version_str(ha, eiter->a.fw_version);
+ ha->isp_ops->fw_version_str(vha, eiter->a.fw_version);
alen = strlen(eiter->a.fw_version);
alen += (alen & 3) ? (4 - (alen & 3)) : 4;
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): FIRMWAREVER=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): FIRMWAREVER=%s.\n", __func__, vha->host_no,
eiter->a.fw_version));
/* Update MS request size. */
- qla2x00_update_ms_fdmi_iocb(ha, size + 16);
+ qla2x00_update_ms_fdmi_iocb(vha, size + 16);
DEBUG13(printk("%s(%ld): RHBA identifier="
"%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", __func__,
- ha->host_no, ct_req->req.rhba.hba_identifier[0],
+ vha->host_no, ct_req->req.rhba.hba_identifier[0],
ct_req->req.rhba.hba_identifier[1],
ct_req->req.rhba.hba_identifier[2],
ct_req->req.rhba.hba_identifier[3],
@@ -1399,25 +1405,25 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
DEBUG13(qla2x00_dump_buffer(entries, size));
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RHBA issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RHBA") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RHBA") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
if (ct_rsp->header.reason_code == CT_REASON_CANNOT_PERFORM &&
ct_rsp->header.explanation_code ==
CT_EXPL_ALREADY_REGISTERED) {
DEBUG2_13(printk("%s(%ld): HBA already registered.\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
rval = QLA_ALREADY_REGISTERED;
}
} else {
DEBUG2(printk("scsi(%ld): RHBA exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1430,17 +1436,17 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_fdmi_dhba(scsi_qla_host_t *ha)
+qla2x00_fdmi_dhba(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
/* Issue RPA */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(ha, DHBA_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, DHBA_REQ_SIZE,
DHBA_RSP_SIZE);
/* Prepare CT request */
@@ -1449,28 +1455,28 @@ qla2x00_fdmi_dhba(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare FDMI command arguments -- portname. */
- memcpy(ct_req->req.dhba.port_name, ha->port_name, WWN_SIZE);
+ memcpy(ct_req->req.dhba.port_name, vha->port_name, WWN_SIZE);
DEBUG13(printk("%s(%ld): DHBA portname="
- "%02x%02x%02x%02x%02x%02x%02x%02x.\n", __func__, ha->host_no,
+ "%02x%02x%02x%02x%02x%02x%02x%02x.\n", __func__, vha->host_no,
ct_req->req.dhba.port_name[0], ct_req->req.dhba.port_name[1],
ct_req->req.dhba.port_name[2], ct_req->req.dhba.port_name[3],
ct_req->req.dhba.port_name[4], ct_req->req.dhba.port_name[5],
ct_req->req.dhba.port_name[6], ct_req->req.dhba.port_name[7]));
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): DHBA issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "DHBA") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "DHBA") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): DHBA exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1483,11 +1489,11 @@ qla2x00_fdmi_dhba(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
+qla2x00_fdmi_rpa(scsi_qla_host_t *vha)
{
int rval, alen;
uint32_t size, max_frame_size;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
@@ -1498,7 +1504,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
/* Issue RPA */
/* Prepare common MS IOCB */
/* Request size adjusted after CT preparation */
- ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(ha, 0, RPA_RSP_SIZE);
+ ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, 0, RPA_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RPA_CMD,
@@ -1506,7 +1512,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare FDMI command arguments -- attribute block, attributes. */
- memcpy(ct_req->req.rpa.port_name, ha->port_name, WWN_SIZE);
+ memcpy(ct_req->req.rpa.port_name, vha->port_name, WWN_SIZE);
size = WWN_SIZE + 4;
/* Attributes */
@@ -1521,8 +1527,9 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
eiter->a.fc4_types[2] = 0x01;
size += 4 + 32;
- DEBUG13(printk("%s(%ld): FC4_TYPES=%02x %02x.\n", __func__, ha->host_no,
- eiter->a.fc4_types[2], eiter->a.fc4_types[1]));
+ DEBUG13(printk("%s(%ld): FC4_TYPES=%02x %02x.\n", __func__,
+ vha->host_no, eiter->a.fc4_types[2],
+ eiter->a.fc4_types[1]));
/* Supported speed. */
eiter = (struct ct_fdmi_port_attr *) (entries + size);
@@ -1544,7 +1551,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
FDMI_PORT_SPEED_1GB);
size += 4 + 4;
- DEBUG13(printk("%s(%ld): SUPPORTED_SPEED=%x.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): SUPPORTED_SPEED=%x.\n", __func__, vha->host_no,
eiter->a.sup_speed));
/* Current speed. */
@@ -1575,7 +1582,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
}
size += 4 + 4;
- DEBUG13(printk("%s(%ld): CURRENT_SPEED=%x.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): CURRENT_SPEED=%x.\n", __func__, vha->host_no,
eiter->a.cur_speed));
/* Max frame size. */
@@ -1588,7 +1595,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
eiter->a.max_frame_size = cpu_to_be32(max_frame_size);
size += 4 + 4;
- DEBUG13(printk("%s(%ld): MAX_FRAME_SIZE=%x.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): MAX_FRAME_SIZE=%x.\n", __func__, vha->host_no,
eiter->a.max_frame_size));
/* OS device name. */
@@ -1600,32 +1607,32 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): OS_DEVICE_NAME=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): OS_DEVICE_NAME=%s.\n", __func__, vha->host_no,
eiter->a.os_dev_name));
/* Hostname. */
- if (strlen(fc_host_system_hostname(ha->host))) {
+ if (strlen(fc_host_system_hostname(vha->host))) {
ct_req->req.rpa.attrs.count =
__constant_cpu_to_be32(FDMI_PORT_ATTR_COUNT);
eiter = (struct ct_fdmi_port_attr *) (entries + size);
eiter->type = __constant_cpu_to_be16(FDMI_PORT_HOST_NAME);
snprintf(eiter->a.host_name, sizeof(eiter->a.host_name),
- "%s", fc_host_system_hostname(ha->host));
+ "%s", fc_host_system_hostname(vha->host));
alen = strlen(eiter->a.host_name);
alen += (alen & 3) ? (4 - (alen & 3)) : 4;
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
DEBUG13(printk("%s(%ld): HOSTNAME=%s.\n", __func__,
- ha->host_no, eiter->a.host_name));
+ vha->host_no, eiter->a.host_name));
}
/* Update MS request size. */
- qla2x00_update_ms_fdmi_iocb(ha, size + 16);
+ qla2x00_update_ms_fdmi_iocb(vha, size + 16);
DEBUG13(printk("%s(%ld): RPA portname="
"%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", __func__,
- ha->host_no, ct_req->req.rpa.port_name[0],
+ vha->host_no, ct_req->req.rpa.port_name[0],
ct_req->req.rpa.port_name[1], ct_req->req.rpa.port_name[2],
ct_req->req.rpa.port_name[3], ct_req->req.rpa.port_name[4],
ct_req->req.rpa.port_name[5], ct_req->req.rpa.port_name[6],
@@ -1633,18 +1640,18 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
DEBUG13(qla2x00_dump_buffer(entries, size));
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RPA issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RPA") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RPA") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RPA exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1657,34 +1664,28 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2x00_fdmi_register(scsi_qla_host_t *ha)
+qla2x00_fdmi_register(scsi_qla_host_t *vha)
{
int rval;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- DEBUG2(printk("scsi(%ld): FDMI unsupported on "
- "ISP2100/ISP2200.\n", ha->host_no));
- return QLA_SUCCESS;
- }
-
- rval = qla2x00_mgmt_svr_login(ha);
+ rval = qla2x00_mgmt_svr_login(vha);
if (rval)
return rval;
- rval = qla2x00_fdmi_rhba(ha);
+ rval = qla2x00_fdmi_rhba(vha);
if (rval) {
if (rval != QLA_ALREADY_REGISTERED)
return rval;
- rval = qla2x00_fdmi_dhba(ha);
+ rval = qla2x00_fdmi_dhba(vha);
if (rval)
return rval;
- rval = qla2x00_fdmi_rhba(ha);
+ rval = qla2x00_fdmi_rhba(vha);
if (rval)
return rval;
}
- rval = qla2x00_fdmi_rpa(ha);
+ rval = qla2x00_fdmi_rpa(vha);
return rval;
}
@@ -1697,11 +1698,11 @@ qla2x00_fdmi_register(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
uint16_t i;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
@@ -1712,7 +1713,7 @@ qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list)
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GFPN_ID */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GFPN_ID_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GFPN_ID_REQ_SIZE,
GFPN_ID_RSP_SIZE);
/* Prepare CT request */
@@ -1726,13 +1727,13 @@ qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list)
ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GFPN_ID issue IOCB "
- "failed (%d).\n", ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp,
+ "failed (%d).\n", vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
"GFPN_ID") != QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
@@ -1750,17 +1751,17 @@ qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list)
}
static inline void *
-qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *ha, uint32_t req_size,
+qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *vha, uint32_t req_size,
uint32_t rsp_size)
{
struct ct_entry_24xx *ct_pkt;
-
+ struct qla_hw_data *ha = vha->hw;
ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
ct_pkt->entry_type = CT_IOCB_TYPE;
ct_pkt->entry_count = 1;
- ct_pkt->nport_handle = cpu_to_le16(ha->mgmt_svr_loop_id);
+ ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id);
ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1);
@@ -1774,7 +1775,7 @@ qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *ha, uint32_t req_size,
ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
- ct_pkt->vp_index = ha->vp_idx;
+ ct_pkt->vp_index = vha->vp_idx;
return ct_pkt;
}
@@ -1803,11 +1804,11 @@ qla24xx_prep_ct_fm_req(struct ct_sns_req *ct_req, uint16_t cmd,
* Returns 0 on success.
*/
int
-qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
uint16_t i;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
@@ -1817,14 +1818,14 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list)
if (!ha->flags.gpsc_supported)
return QLA_FUNCTION_FAILED;
- rval = qla2x00_mgmt_svr_login(ha);
+ rval = qla2x00_mgmt_svr_login(vha);
if (rval)
return rval;
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GFPN_ID */
/* Prepare common MS IOCB */
- ms_pkt = qla24xx_prep_ms_fm_iocb(ha, GPSC_REQ_SIZE,
+ ms_pkt = qla24xx_prep_ms_fm_iocb(vha, GPSC_REQ_SIZE,
GPSC_RSP_SIZE);
/* Prepare CT request */
@@ -1837,13 +1838,13 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list)
WWN_SIZE);
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GPSC issue IOCB "
- "failed (%d).\n", ha->host_no, rval));
- } else if ((rval = qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp,
+ "failed (%d).\n", vha->host_no, rval));
+ } else if ((rval = qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
"GPSC")) != QLA_SUCCESS) {
/* FM command unsupported? */
if (rval == QLA_INVALID_COMMAND &&
@@ -1853,7 +1854,7 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list)
CT_REASON_COMMAND_UNSUPPORTED)) {
DEBUG2(printk("scsi(%ld): GPSC command "
"unsupported, disabling query...\n",
- ha->host_no));
+ vha->host_no));
ha->flags.gpsc_supported = 0;
rval = QLA_FUNCTION_FAILED;
break;
@@ -1878,7 +1879,7 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list)
DEBUG2_3(printk("scsi(%ld): GPSC ext entry - "
"fpn %02x%02x%02x%02x%02x%02x%02x%02x speeds=%04x "
- "speed=%04x.\n", ha->host_no,
+ "speed=%04x.\n", vha->host_no,
list[i].fabric_port_name[0],
list[i].fabric_port_name[1],
list[i].fabric_port_name[2],
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 4218f20f5ed5..52ed56ecf195 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -5,6 +5,7 @@
* See LICENSE.qla2xxx for copyright and licensing details.
*/
#include "qla_def.h"
+#include "qla_gbl.h"
#include <linux/delay.h>
#include <linux/vmalloc.h>
@@ -21,7 +22,6 @@
static int qla2x00_isp_firmware(scsi_qla_host_t *);
static void qla2x00_resize_request_q(scsi_qla_host_t *);
static int qla2x00_setup_chip(scsi_qla_host_t *);
-static void qla2x00_init_response_q_entries(scsi_qla_host_t *);
static int qla2x00_init_rings(scsi_qla_host_t *);
static int qla2x00_fw_ready(scsi_qla_host_t *);
static int qla2x00_configure_hba(scsi_qla_host_t *);
@@ -35,10 +35,11 @@ static int qla2x00_fabric_dev_login(scsi_qla_host_t *, fc_port_t *,
static int qla2x00_restart_isp(scsi_qla_host_t *);
-static int qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev);
+static int qla2x00_find_new_loop_id(scsi_qla_host_t *, fc_port_t *);
static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *);
static int qla84xx_init_chip(scsi_qla_host_t *);
+static int qla25xx_init_queues(struct qla_hw_data *);
/****************************************************************************/
/* QLogic ISP2x00 Hardware Support Functions. */
@@ -55,77 +56,81 @@ static int qla84xx_init_chip(scsi_qla_host_t *);
* 0 = success
*/
int
-qla2x00_initialize_adapter(scsi_qla_host_t *ha)
+qla2x00_initialize_adapter(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
/* Clear adapter flags. */
- ha->flags.online = 0;
- ha->flags.reset_active = 0;
- atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
- atomic_set(&ha->loop_state, LOOP_DOWN);
- ha->device_flags = DFLG_NO_CABLE;
- ha->dpc_flags = 0;
- ha->flags.management_server_logged_in = 0;
- ha->marker_needed = 0;
+ vha->flags.online = 0;
+ vha->flags.reset_active = 0;
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ vha->device_flags = DFLG_NO_CABLE;
+ vha->dpc_flags = 0;
+ vha->flags.management_server_logged_in = 0;
+ vha->marker_needed = 0;
ha->mbx_flags = 0;
ha->isp_abort_cnt = 0;
ha->beacon_blink_led = 0;
- set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
+
+ set_bit(0, ha->req_qid_map);
+ set_bit(0, ha->rsp_qid_map);
qla_printk(KERN_INFO, ha, "Configuring PCI space...\n");
- rval = ha->isp_ops->pci_config(ha);
+ rval = ha->isp_ops->pci_config(vha);
if (rval) {
DEBUG2(printk("scsi(%ld): Unable to configure PCI space.\n",
- ha->host_no));
+ vha->host_no));
return (rval);
}
- ha->isp_ops->reset_chip(ha);
+ ha->isp_ops->reset_chip(vha);
- rval = qla2xxx_get_flash_info(ha);
+ rval = qla2xxx_get_flash_info(vha);
if (rval) {
DEBUG2(printk("scsi(%ld): Unable to validate FLASH data.\n",
- ha->host_no));
+ vha->host_no));
return (rval);
}
- ha->isp_ops->get_flash_version(ha, ha->request_ring);
+ ha->isp_ops->get_flash_version(vha, req->ring);
qla_printk(KERN_INFO, ha, "Configure NVRAM parameters...\n");
- ha->isp_ops->nvram_config(ha);
+ ha->isp_ops->nvram_config(vha);
if (ha->flags.disable_serdes) {
/* Mask HBA via NVRAM settings? */
qla_printk(KERN_INFO, ha, "Masking HBA WWPN "
"%02x%02x%02x%02x%02x%02x%02x%02x (via NVRAM).\n",
- ha->port_name[0], ha->port_name[1],
- ha->port_name[2], ha->port_name[3],
- ha->port_name[4], ha->port_name[5],
- ha->port_name[6], ha->port_name[7]);
+ vha->port_name[0], vha->port_name[1],
+ vha->port_name[2], vha->port_name[3],
+ vha->port_name[4], vha->port_name[5],
+ vha->port_name[6], vha->port_name[7]);
return QLA_FUNCTION_FAILED;
}
qla_printk(KERN_INFO, ha, "Verifying loaded RISC code...\n");
- if (qla2x00_isp_firmware(ha) != QLA_SUCCESS) {
- rval = ha->isp_ops->chip_diag(ha);
+ if (qla2x00_isp_firmware(vha) != QLA_SUCCESS) {
+ rval = ha->isp_ops->chip_diag(vha);
if (rval)
return (rval);
- rval = qla2x00_setup_chip(ha);
+ rval = qla2x00_setup_chip(vha);
if (rval)
return (rval);
}
if (IS_QLA84XX(ha)) {
- ha->cs84xx = qla84xx_get_chip(ha);
+ ha->cs84xx = qla84xx_get_chip(vha);
if (!ha->cs84xx) {
qla_printk(KERN_ERR, ha,
"Unable to configure ISP84XX.\n");
return QLA_FUNCTION_FAILED;
}
}
- rval = qla2x00_init_rings(ha);
+ rval = qla2x00_init_rings(vha);
return (rval);
}
@@ -137,10 +142,11 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2100_pci_config(scsi_qla_host_t *ha)
+qla2100_pci_config(scsi_qla_host_t *vha)
{
uint16_t w;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
pci_set_master(ha->pdev);
@@ -167,11 +173,12 @@ qla2100_pci_config(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2300_pci_config(scsi_qla_host_t *ha)
+qla2300_pci_config(scsi_qla_host_t *vha)
{
uint16_t w;
unsigned long flags = 0;
uint32_t cnt;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
pci_set_master(ha->pdev);
@@ -248,10 +255,11 @@ qla2300_pci_config(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla24xx_pci_config(scsi_qla_host_t *ha)
+qla24xx_pci_config(scsi_qla_host_t *vha)
{
uint16_t w;
unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
pci_set_master(ha->pdev);
@@ -291,9 +299,10 @@ qla24xx_pci_config(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla25xx_pci_config(scsi_qla_host_t *ha)
+qla25xx_pci_config(scsi_qla_host_t *vha)
{
uint16_t w;
+ struct qla_hw_data *ha = vha->hw;
pci_set_master(ha->pdev);
pci_try_set_mwi(ha->pdev);
@@ -321,32 +330,33 @@ qla25xx_pci_config(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_isp_firmware(scsi_qla_host_t *ha)
+qla2x00_isp_firmware(scsi_qla_host_t *vha)
{
int rval;
uint16_t loop_id, topo, sw_cap;
uint8_t domain, area, al_pa;
+ struct qla_hw_data *ha = vha->hw;
/* Assume loading risc code */
rval = QLA_FUNCTION_FAILED;
if (ha->flags.disable_risc_code_load) {
DEBUG2(printk("scsi(%ld): RISC CODE NOT loaded\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_INFO, ha, "RISC CODE NOT loaded\n");
/* Verify checksum of loaded RISC code. */
- rval = qla2x00_verify_checksum(ha, ha->fw_srisc_address);
+ rval = qla2x00_verify_checksum(vha, ha->fw_srisc_address);
if (rval == QLA_SUCCESS) {
/* And, verify we are not in ROM code. */
- rval = qla2x00_get_adapter_id(ha, &loop_id, &al_pa,
+ rval = qla2x00_get_adapter_id(vha, &loop_id, &al_pa,
&area, &domain, &topo, &sw_cap);
}
}
if (rval) {
DEBUG2_3(printk("scsi(%ld): **** Load RISC code ****\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -359,9 +369,10 @@ qla2x00_isp_firmware(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
void
-qla2x00_reset_chip(scsi_qla_host_t *ha)
+qla2x00_reset_chip(scsi_qla_host_t *vha)
{
unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
uint32_t cnt;
uint16_t cmd;
@@ -499,10 +510,11 @@ qla2x00_reset_chip(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static inline void
-qla24xx_reset_risc(scsi_qla_host_t *ha)
+qla24xx_reset_risc(scsi_qla_host_t *vha)
{
int hw_evt = 0;
unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
uint32_t cnt, d2;
uint16_t wd;
@@ -541,7 +553,7 @@ qla24xx_reset_risc(scsi_qla_host_t *ha)
barrier();
}
if (cnt == 0 || hw_evt)
- qla2xxx_hw_event_log(ha, HW_EVENT_RESET_ERR,
+ qla2xxx_hw_event_log(vha, HW_EVENT_RESET_ERR,
RD_REG_WORD(&reg->mailbox1), RD_REG_WORD(&reg->mailbox2),
RD_REG_WORD(&reg->mailbox3));
@@ -571,12 +583,13 @@ qla24xx_reset_risc(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
void
-qla24xx_reset_chip(scsi_qla_host_t *ha)
+qla24xx_reset_chip(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
ha->isp_ops->disable_intrs(ha);
/* Perform RISC reset. */
- qla24xx_reset_risc(ha);
+ qla24xx_reset_risc(vha);
}
/**
@@ -586,20 +599,22 @@ qla24xx_reset_chip(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2x00_chip_diag(scsi_qla_host_t *ha)
+qla2x00_chip_diag(scsi_qla_host_t *vha)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
unsigned long flags = 0;
uint16_t data;
uint32_t cnt;
uint16_t mb[5];
+ struct req_que *req = ha->req_q_map[0];
/* Assume a failed state */
rval = QLA_FUNCTION_FAILED;
DEBUG3(printk("scsi(%ld): Testing device at %lx.\n",
- ha->host_no, (u_long)&reg->flash_address));
+ vha->host_no, (u_long)&reg->flash_address));
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -662,17 +677,17 @@ qla2x00_chip_diag(scsi_qla_host_t *ha)
ha->product_id[3] = mb[4];
/* Adjust fw RISC transfer size */
- if (ha->request_q_length > 1024)
+ if (req->length > 1024)
ha->fw_transfer_size = REQUEST_ENTRY_SIZE * 1024;
else
ha->fw_transfer_size = REQUEST_ENTRY_SIZE *
- ha->request_q_length;
+ req->length;
if (IS_QLA2200(ha) &&
RD_MAILBOX_REG(ha, reg, 7) == QLA2200A_RISC_ROM_VER) {
/* Limit firmware transfer size with a 2200A */
DEBUG3(printk("scsi(%ld): Found QLA2200A chip.\n",
- ha->host_no));
+ vha->host_no));
ha->device_type |= DT_ISP2200A;
ha->fw_transfer_size = 128;
@@ -681,11 +696,11 @@ qla2x00_chip_diag(scsi_qla_host_t *ha)
/* Wrap Incoming Mailboxes Test. */
spin_unlock_irqrestore(&ha->hardware_lock, flags);
- DEBUG3(printk("scsi(%ld): Checking mailboxes.\n", ha->host_no));
- rval = qla2x00_mbx_reg_test(ha);
+ DEBUG3(printk("scsi(%ld): Checking mailboxes.\n", vha->host_no));
+ rval = qla2x00_mbx_reg_test(vha);
if (rval) {
DEBUG(printk("scsi(%ld): Failed mailbox send register test\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha,
"Failed mailbox send register test\n");
}
@@ -698,7 +713,7 @@ qla2x00_chip_diag(scsi_qla_host_t *ha)
chip_diag_failed:
if (rval)
DEBUG2_3(printk("scsi(%ld): Chip diagnostics **** FAILED "
- "****\n", ha->host_no));
+ "****\n", vha->host_no));
spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -712,19 +727,21 @@ chip_diag_failed:
* Returns 0 on success.
*/
int
-qla24xx_chip_diag(scsi_qla_host_t *ha)
+qla24xx_chip_diag(scsi_qla_host_t *vha)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
/* Perform RISC reset. */
- qla24xx_reset_risc(ha);
+ qla24xx_reset_risc(vha);
- ha->fw_transfer_size = REQUEST_ENTRY_SIZE * ha->request_q_length;
+ ha->fw_transfer_size = REQUEST_ENTRY_SIZE * req->length;
- rval = qla2x00_mbx_reg_test(ha);
+ rval = qla2x00_mbx_reg_test(vha);
if (rval) {
DEBUG(printk("scsi(%ld): Failed mailbox send register test\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha,
"Failed mailbox send register test\n");
} else {
@@ -736,13 +753,16 @@ qla24xx_chip_diag(scsi_qla_host_t *ha)
}
void
-qla2x00_alloc_fw_dump(scsi_qla_host_t *ha)
+qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
{
int rval;
uint32_t dump_size, fixed_size, mem_size, req_q_size, rsp_q_size,
- eft_size, fce_size;
+ eft_size, fce_size, mq_size;
dma_addr_t tc_dma;
void *tc;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
if (ha->fw_dump) {
qla_printk(KERN_WARNING, ha,
@@ -751,7 +771,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha)
}
ha->fw_dumped = 0;
- fixed_size = mem_size = eft_size = fce_size = 0;
+ fixed_size = mem_size = eft_size = fce_size = mq_size = 0;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
fixed_size = sizeof(struct qla2100_fw_dump);
} else if (IS_QLA23XX(ha)) {
@@ -760,10 +780,12 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha)
sizeof(uint16_t);
} else if (IS_FWI2_CAPABLE(ha)) {
fixed_size = IS_QLA25XX(ha) ?
- offsetof(struct qla25xx_fw_dump, ext_mem):
- offsetof(struct qla24xx_fw_dump, ext_mem);
+ offsetof(struct qla25xx_fw_dump, ext_mem) :
+ offsetof(struct qla24xx_fw_dump, ext_mem);
mem_size = (ha->fw_memory_size - 0x100000 + 1) *
sizeof(uint32_t);
+ if (ha->mqenable)
+ mq_size = sizeof(struct qla2xxx_mq_chain);
/* Allocate memory for Fibre Channel Event Buffer. */
if (!IS_QLA25XX(ha))
@@ -778,7 +800,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha)
}
memset(tc, 0, FCE_SIZE);
- rval = qla2x00_enable_fce_trace(ha, tc_dma, FCE_NUM_BUFFERS,
+ rval = qla2x00_enable_fce_trace(vha, tc_dma, FCE_NUM_BUFFERS,
ha->fce_mb, &ha->fce_bufs);
if (rval) {
qla_printk(KERN_WARNING, ha, "Unable to initialize "
@@ -807,7 +829,7 @@ try_eft:
}
memset(tc, 0, EFT_SIZE);
- rval = qla2x00_enable_eft_trace(ha, tc_dma, EFT_NUM_BUFFERS);
+ rval = qla2x00_enable_eft_trace(vha, tc_dma, EFT_NUM_BUFFERS);
if (rval) {
qla_printk(KERN_WARNING, ha, "Unable to initialize "
"EFT (%d).\n", rval);
@@ -824,12 +846,12 @@ try_eft:
ha->eft = tc;
}
cont_alloc:
- req_q_size = ha->request_q_length * sizeof(request_t);
- rsp_q_size = ha->response_q_length * sizeof(response_t);
+ req_q_size = req->length * sizeof(request_t);
+ rsp_q_size = rsp->length * sizeof(response_t);
dump_size = offsetof(struct qla2xxx_fw_dump, isp);
dump_size += fixed_size + mem_size + req_q_size + rsp_q_size +
- eft_size + fce_size;
+ mq_size + eft_size + fce_size;
ha->fw_dump = vmalloc(dump_size);
if (!ha->fw_dump) {
@@ -844,7 +866,6 @@ cont_alloc:
}
return;
}
-
qla_printk(KERN_INFO, ha, "Allocated (%d KB) for firmware dump...\n",
dump_size / 1024);
@@ -875,27 +896,29 @@ cont_alloc:
* Returns 0 on success.
*/
static void
-qla2x00_resize_request_q(scsi_qla_host_t *ha)
+qla2x00_resize_request_q(scsi_qla_host_t *vha)
{
int rval;
uint16_t fw_iocb_cnt = 0;
uint16_t request_q_length = REQUEST_ENTRY_CNT_2XXX_EXT_MEM;
dma_addr_t request_dma;
request_t *request_ring;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
/* Valid only on recent ISPs. */
if (IS_QLA2100(ha) || IS_QLA2200(ha))
return;
/* Retrieve IOCB counts available to the firmware. */
- rval = qla2x00_get_resource_cnts(ha, NULL, NULL, NULL, &fw_iocb_cnt,
- &ha->max_npiv_vports);
+ rval = qla2x00_get_resource_cnts(vha, NULL, NULL, NULL, &fw_iocb_cnt,
+ &ha->max_npiv_vports);
if (rval)
return;
/* No point in continuing if current settings are sufficient. */
if (fw_iocb_cnt < 1024)
return;
- if (ha->request_q_length >= request_q_length)
+ if (req->length >= request_q_length)
return;
/* Attempt to claim larger area for request queue. */
@@ -909,17 +932,17 @@ qla2x00_resize_request_q(scsi_qla_host_t *ha)
qla_printk(KERN_INFO, ha, "Extended memory detected (%d KB)...\n",
(ha->fw_memory_size + 1) / 1024);
qla_printk(KERN_INFO, ha, "Resizing request queue depth "
- "(%d -> %d)...\n", ha->request_q_length, request_q_length);
+ "(%d -> %d)...\n", req->length, request_q_length);
/* Clear old allocations. */
dma_free_coherent(&ha->pdev->dev,
- (ha->request_q_length + 1) * sizeof(request_t), ha->request_ring,
- ha->request_dma);
+ (req->length + 1) * sizeof(request_t), req->ring,
+ req->dma);
/* Begin using larger queue. */
- ha->request_q_length = request_q_length;
- ha->request_ring = request_ring;
- ha->request_dma = request_dma;
+ req->length = request_q_length;
+ req->ring = request_ring;
+ req->dma = request_dma;
}
/**
@@ -929,10 +952,11 @@ qla2x00_resize_request_q(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_setup_chip(scsi_qla_host_t *ha)
+qla2x00_setup_chip(scsi_qla_host_t *vha)
{
int rval;
uint32_t srisc_address = 0;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
unsigned long flags;
@@ -945,28 +969,27 @@ qla2x00_setup_chip(scsi_qla_host_t *ha)
}
/* Load firmware sequences */
- rval = ha->isp_ops->load_risc(ha, &srisc_address);
+ rval = ha->isp_ops->load_risc(vha, &srisc_address);
if (rval == QLA_SUCCESS) {
DEBUG(printk("scsi(%ld): Verifying Checksum of loaded RISC "
- "code.\n", ha->host_no));
+ "code.\n", vha->host_no));
- rval = qla2x00_verify_checksum(ha, srisc_address);
+ rval = qla2x00_verify_checksum(vha, srisc_address);
if (rval == QLA_SUCCESS) {
/* Start firmware execution. */
DEBUG(printk("scsi(%ld): Checksum OK, start "
- "firmware.\n", ha->host_no));
+ "firmware.\n", vha->host_no));
- rval = qla2x00_execute_fw(ha, srisc_address);
+ rval = qla2x00_execute_fw(vha, srisc_address);
/* Retrieve firmware information. */
if (rval == QLA_SUCCESS && ha->fw_major_version == 0) {
- qla2x00_get_fw_version(ha,
+ qla2x00_get_fw_version(vha,
&ha->fw_major_version,
&ha->fw_minor_version,
&ha->fw_subminor_version,
&ha->fw_attributes, &ha->fw_memory_size);
ha->flags.npiv_supported = 0;
- if ((IS_QLA24XX(ha) || IS_QLA25XX(ha) ||
- IS_QLA84XX(ha)) &&
+ if (IS_QLA2XXX_MIDTYPE(ha) &&
(ha->fw_attributes & BIT_2)) {
ha->flags.npiv_supported = 1;
if ((!ha->max_npiv_vports) ||
@@ -975,15 +998,15 @@ qla2x00_setup_chip(scsi_qla_host_t *ha)
ha->max_npiv_vports =
MIN_MULTI_ID_FABRIC - 1;
}
- qla2x00_resize_request_q(ha);
+ qla2x00_resize_request_q(vha);
if (ql2xallocfwdump)
- qla2x00_alloc_fw_dump(ha);
+ qla2x00_alloc_fw_dump(vha);
}
} else {
DEBUG2(printk(KERN_INFO
"scsi(%ld): ISP Firmware failed checksum.\n",
- ha->host_no));
+ vha->host_no));
}
}
@@ -1002,7 +1025,7 @@ qla2x00_setup_chip(scsi_qla_host_t *ha)
if (rval) {
DEBUG2_3(printk("scsi(%ld): Setup chip **** FAILED ****.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -1017,14 +1040,14 @@ qla2x00_setup_chip(scsi_qla_host_t *ha)
*
* Returns 0 on success.
*/
-static void
-qla2x00_init_response_q_entries(scsi_qla_host_t *ha)
+void
+qla2x00_init_response_q_entries(struct rsp_que *rsp)
{
uint16_t cnt;
response_t *pkt;
- pkt = ha->response_ring_ptr;
- for (cnt = 0; cnt < ha->response_q_length; cnt++) {
+ pkt = rsp->ring_ptr;
+ for (cnt = 0; cnt < rsp->length; cnt++) {
pkt->signature = RESPONSE_PROCESSED;
pkt++;
}
@@ -1038,19 +1061,20 @@ qla2x00_init_response_q_entries(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
void
-qla2x00_update_fw_options(scsi_qla_host_t *ha)
+qla2x00_update_fw_options(scsi_qla_host_t *vha)
{
uint16_t swing, emphasis, tx_sens, rx_sens;
+ struct qla_hw_data *ha = vha->hw;
memset(ha->fw_options, 0, sizeof(ha->fw_options));
- qla2x00_get_fw_options(ha, ha->fw_options);
+ qla2x00_get_fw_options(vha, ha->fw_options);
if (IS_QLA2100(ha) || IS_QLA2200(ha))
return;
/* Serial Link options. */
DEBUG3(printk("scsi(%ld): Serial link options:\n",
- ha->host_no));
+ vha->host_no));
DEBUG3(qla2x00_dump_buffer((uint8_t *)&ha->fw_seriallink_options,
sizeof(ha->fw_seriallink_options)));
@@ -1108,19 +1132,20 @@ qla2x00_update_fw_options(scsi_qla_host_t *ha)
ha->fw_options[2] |= BIT_13;
/* Update firmware options. */
- qla2x00_set_fw_options(ha, ha->fw_options);
+ qla2x00_set_fw_options(vha, ha->fw_options);
}
void
-qla24xx_update_fw_options(scsi_qla_host_t *ha)
+qla24xx_update_fw_options(scsi_qla_host_t *vha)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
/* Update Serial Link options. */
if ((le16_to_cpu(ha->fw_seriallink_options24[0]) & BIT_0) == 0)
return;
- rval = qla2x00_set_serdes_params(ha,
+ rval = qla2x00_set_serdes_params(vha,
le16_to_cpu(ha->fw_seriallink_options24[1]),
le16_to_cpu(ha->fw_seriallink_options24[2]),
le16_to_cpu(ha->fw_seriallink_options24[3]));
@@ -1131,19 +1156,22 @@ qla24xx_update_fw_options(scsi_qla_host_t *ha)
}
void
-qla2x00_config_rings(struct scsi_qla_host *ha)
+qla2x00_config_rings(struct scsi_qla_host *vha)
{
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
/* Setup ring parameters in initialization control block. */
ha->init_cb->request_q_outpointer = __constant_cpu_to_le16(0);
ha->init_cb->response_q_inpointer = __constant_cpu_to_le16(0);
- ha->init_cb->request_q_length = cpu_to_le16(ha->request_q_length);
- ha->init_cb->response_q_length = cpu_to_le16(ha->response_q_length);
- ha->init_cb->request_q_address[0] = cpu_to_le32(LSD(ha->request_dma));
- ha->init_cb->request_q_address[1] = cpu_to_le32(MSD(ha->request_dma));
- ha->init_cb->response_q_address[0] = cpu_to_le32(LSD(ha->response_dma));
- ha->init_cb->response_q_address[1] = cpu_to_le32(MSD(ha->response_dma));
+ ha->init_cb->request_q_length = cpu_to_le16(req->length);
+ ha->init_cb->response_q_length = cpu_to_le16(rsp->length);
+ ha->init_cb->request_q_address[0] = cpu_to_le32(LSD(req->dma));
+ ha->init_cb->request_q_address[1] = cpu_to_le32(MSD(req->dma));
+ ha->init_cb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma));
+ ha->init_cb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma));
WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), 0);
WRT_REG_WORD(ISP_REQ_Q_OUT(ha, reg), 0);
@@ -1153,27 +1181,62 @@ qla2x00_config_rings(struct scsi_qla_host *ha)
}
void
-qla24xx_config_rings(struct scsi_qla_host *ha)
+qla24xx_config_rings(struct scsi_qla_host *vha)
{
- struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ struct qla_hw_data *ha = vha->hw;
+ device_reg_t __iomem *reg = ISP_QUE_REG(ha, 0);
+ struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp;
+ struct qla_msix_entry *msix;
struct init_cb_24xx *icb;
+ uint16_t rid = 0;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
- /* Setup ring parameters in initialization control block. */
+/* Setup ring parameters in initialization control block. */
icb = (struct init_cb_24xx *)ha->init_cb;
icb->request_q_outpointer = __constant_cpu_to_le16(0);
icb->response_q_inpointer = __constant_cpu_to_le16(0);
- icb->request_q_length = cpu_to_le16(ha->request_q_length);
- icb->response_q_length = cpu_to_le16(ha->response_q_length);
- icb->request_q_address[0] = cpu_to_le32(LSD(ha->request_dma));
- icb->request_q_address[1] = cpu_to_le32(MSD(ha->request_dma));
- icb->response_q_address[0] = cpu_to_le32(LSD(ha->response_dma));
- icb->response_q_address[1] = cpu_to_le32(MSD(ha->response_dma));
-
- WRT_REG_DWORD(&reg->req_q_in, 0);
- WRT_REG_DWORD(&reg->req_q_out, 0);
- WRT_REG_DWORD(&reg->rsp_q_in, 0);
- WRT_REG_DWORD(&reg->rsp_q_out, 0);
- RD_REG_DWORD(&reg->rsp_q_out);
+ icb->request_q_length = cpu_to_le16(req->length);
+ icb->response_q_length = cpu_to_le16(rsp->length);
+ icb->request_q_address[0] = cpu_to_le32(LSD(req->dma));
+ icb->request_q_address[1] = cpu_to_le32(MSD(req->dma));
+ icb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma));
+ icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma));
+
+ if (ha->mqenable) {
+ icb->qos = __constant_cpu_to_le16(QLA_DEFAULT_QUE_QOS);
+ icb->rid = __constant_cpu_to_le16(rid);
+ if (ha->flags.msix_enabled) {
+ msix = &ha->msix_entries[1];
+ DEBUG2_17(printk(KERN_INFO
+ "Reistering vector 0x%x for base que\n", msix->entry));
+ icb->msix = cpu_to_le16(msix->entry);
+ }
+ /* Use alternate PCI bus number */
+ if (MSB(rid))
+ icb->firmware_options_2 |=
+ __constant_cpu_to_le32(BIT_19);
+ /* Use alternate PCI devfn */
+ if (LSB(rid))
+ icb->firmware_options_2 |=
+ __constant_cpu_to_le32(BIT_18);
+
+ icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_22);
+ icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_23);
+ ha->rsp_q_map[0]->options = icb->firmware_options_2;
+
+ WRT_REG_DWORD(&reg->isp25mq.req_q_in, 0);
+ WRT_REG_DWORD(&reg->isp25mq.req_q_out, 0);
+ WRT_REG_DWORD(&reg->isp25mq.rsp_q_in, 0);
+ WRT_REG_DWORD(&reg->isp25mq.rsp_q_out, 0);
+ } else {
+ WRT_REG_DWORD(&reg->isp24.req_q_in, 0);
+ WRT_REG_DWORD(&reg->isp24.req_q_out, 0);
+ WRT_REG_DWORD(&reg->isp24.rsp_q_in, 0);
+ WRT_REG_DWORD(&reg->isp24.rsp_q_out, 0);
+ }
+ /* PCI posting */
+ RD_REG_DWORD(&ioreg->hccr);
}
/**
@@ -1186,11 +1249,14 @@ qla24xx_config_rings(struct scsi_qla_host *ha)
* Returns 0 on success.
*/
static int
-qla2x00_init_rings(scsi_qla_host_t *ha)
+qla2x00_init_rings(scsi_qla_host_t *vha)
{
int rval;
unsigned long flags = 0;
int cnt;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
struct mid_init_cb_24xx *mid_init_cb =
(struct mid_init_cb_24xx *) ha->init_cb;
@@ -1198,45 +1264,45 @@ qla2x00_init_rings(scsi_qla_host_t *ha)
/* Clear outstanding commands array. */
for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS; cnt++)
- ha->outstanding_cmds[cnt] = NULL;
+ req->outstanding_cmds[cnt] = NULL;
- ha->current_outstanding_cmd = 0;
+ req->current_outstanding_cmd = 0;
/* Clear RSCN queue. */
- ha->rscn_in_ptr = 0;
- ha->rscn_out_ptr = 0;
+ vha->rscn_in_ptr = 0;
+ vha->rscn_out_ptr = 0;
/* Initialize firmware. */
- ha->request_ring_ptr = ha->request_ring;
- ha->req_ring_index = 0;
- ha->req_q_cnt = ha->request_q_length;
- ha->response_ring_ptr = ha->response_ring;
- ha->rsp_ring_index = 0;
+ req->ring_ptr = req->ring;
+ req->ring_index = 0;
+ req->cnt = req->length;
+ rsp->ring_ptr = rsp->ring;
+ rsp->ring_index = 0;
/* Initialize response queue entries */
- qla2x00_init_response_q_entries(ha);
+ qla2x00_init_response_q_entries(rsp);
- ha->isp_ops->config_rings(ha);
+ ha->isp_ops->config_rings(vha);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
/* Update any ISP specific firmware options before initialization. */
- ha->isp_ops->update_fw_options(ha);
+ ha->isp_ops->update_fw_options(vha);
- DEBUG(printk("scsi(%ld): Issue init firmware.\n", ha->host_no));
+ DEBUG(printk("scsi(%ld): Issue init firmware.\n", vha->host_no));
if (ha->flags.npiv_supported)
mid_init_cb->count = cpu_to_le16(ha->max_npiv_vports);
mid_init_cb->options = __constant_cpu_to_le16(BIT_1);
- rval = qla2x00_init_firmware(ha, ha->init_cb_size);
+ rval = qla2x00_init_firmware(vha, ha->init_cb_size);
if (rval) {
DEBUG2_3(printk("scsi(%ld): Init firmware **** FAILED ****.\n",
- ha->host_no));
+ vha->host_no));
} else {
DEBUG3(printk("scsi(%ld): Init firmware -- success.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -1249,13 +1315,14 @@ qla2x00_init_rings(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_fw_ready(scsi_qla_host_t *ha)
+qla2x00_fw_ready(scsi_qla_host_t *vha)
{
int rval;
unsigned long wtime, mtime, cs84xx_time;
uint16_t min_wait; /* Minimum wait time if loop is down */
uint16_t wait_time; /* Wait time if loop is coming ready */
uint16_t state[3];
+ struct qla_hw_data *ha = vha->hw;
rval = QLA_SUCCESS;
@@ -1277,29 +1344,29 @@ qla2x00_fw_ready(scsi_qla_host_t *ha)
wtime = jiffies + (wait_time * HZ);
/* Wait for ISP to finish LIP */
- if (!ha->flags.init_done)
+ if (!vha->flags.init_done)
qla_printk(KERN_INFO, ha, "Waiting for LIP to complete...\n");
DEBUG3(printk("scsi(%ld): Waiting for LIP to complete...\n",
- ha->host_no));
+ vha->host_no));
do {
- rval = qla2x00_get_firmware_state(ha, state);
+ rval = qla2x00_get_firmware_state(vha, state);
if (rval == QLA_SUCCESS) {
if (state[0] < FSTATE_LOSS_OF_SYNC) {
- ha->device_flags &= ~DFLG_NO_CABLE;
+ vha->device_flags &= ~DFLG_NO_CABLE;
}
if (IS_QLA84XX(ha) && state[0] != FSTATE_READY) {
DEBUG16(printk("scsi(%ld): fw_state=%x "
- "84xx=%x.\n", ha->host_no, state[0],
+ "84xx=%x.\n", vha->host_no, state[0],
state[2]));
if ((state[2] & FSTATE_LOGGED_IN) &&
(state[2] & FSTATE_WAITING_FOR_VERIFY)) {
DEBUG16(printk("scsi(%ld): Sending "
- "verify iocb.\n", ha->host_no));
+ "verify iocb.\n", vha->host_no));
cs84xx_time = jiffies;
- rval = qla84xx_init_chip(ha);
+ rval = qla84xx_init_chip(vha);
if (rval != QLA_SUCCESS)
break;
@@ -1309,13 +1376,13 @@ qla2x00_fw_ready(scsi_qla_host_t *ha)
mtime += cs84xx_time;
DEBUG16(printk("scsi(%ld): Increasing "
"wait time by %ld. New time %ld\n",
- ha->host_no, cs84xx_time, wtime));
+ vha->host_no, cs84xx_time, wtime));
}
} else if (state[0] == FSTATE_READY) {
DEBUG(printk("scsi(%ld): F/W Ready - OK \n",
- ha->host_no));
+ vha->host_no));
- qla2x00_get_retry_cnt(ha, &ha->retry_count,
+ qla2x00_get_retry_cnt(vha, &ha->retry_count,
&ha->login_timeout, &ha->r_a_tov);
rval = QLA_SUCCESS;
@@ -1324,7 +1391,7 @@ qla2x00_fw_ready(scsi_qla_host_t *ha)
rval = QLA_FUNCTION_FAILED;
- if (atomic_read(&ha->loop_down_timer) &&
+ if (atomic_read(&vha->loop_down_timer) &&
state[0] != FSTATE_READY) {
/* Loop down. Timeout on min_wait for states
* other than Wait for Login.
@@ -1333,7 +1400,7 @@ qla2x00_fw_ready(scsi_qla_host_t *ha)
qla_printk(KERN_INFO, ha,
"Cable is unplugged...\n");
- ha->device_flags |= DFLG_NO_CABLE;
+ vha->device_flags |= DFLG_NO_CABLE;
break;
}
}
@@ -1350,15 +1417,15 @@ qla2x00_fw_ready(scsi_qla_host_t *ha)
msleep(500);
DEBUG3(printk("scsi(%ld): fw_state=%x curr time=%lx.\n",
- ha->host_no, state[0], jiffies));
+ vha->host_no, state[0], jiffies));
} while (1);
DEBUG(printk("scsi(%ld): fw_state=%x curr time=%lx.\n",
- ha->host_no, state[0], jiffies));
+ vha->host_no, state[0], jiffies));
if (rval) {
DEBUG2_3(printk("scsi(%ld): Firmware ready **** FAILED ****.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -1378,7 +1445,7 @@ qla2x00_fw_ready(scsi_qla_host_t *ha)
* Kernel context.
*/
static int
-qla2x00_configure_hba(scsi_qla_host_t *ha)
+qla2x00_configure_hba(scsi_qla_host_t *vha)
{
int rval;
uint16_t loop_id;
@@ -1388,19 +1455,20 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
uint8_t area;
uint8_t domain;
char connect_type[22];
+ struct qla_hw_data *ha = vha->hw;
/* Get host addresses. */
- rval = qla2x00_get_adapter_id(ha,
+ rval = qla2x00_get_adapter_id(vha,
&loop_id, &al_pa, &area, &domain, &topo, &sw_cap);
if (rval != QLA_SUCCESS) {
- if (LOOP_TRANSITION(ha) || atomic_read(&ha->loop_down_timer) ||
+ if (LOOP_TRANSITION(vha) || atomic_read(&ha->loop_down_timer) ||
(rval == QLA_COMMAND_ERROR && loop_id == 0x7)) {
DEBUG2(printk("%s(%ld) Loop is in a transition state\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
} else {
qla_printk(KERN_WARNING, ha,
"ERROR -- Unable to get host loop ID.\n");
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
}
return (rval);
}
@@ -1411,7 +1479,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
return (QLA_FUNCTION_FAILED);
}
- ha->loop_id = loop_id;
+ vha->loop_id = loop_id;
/* initialize */
ha->min_external_loopid = SNS_FIRST_LOOP_ID;
@@ -1421,14 +1489,14 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
switch (topo) {
case 0:
DEBUG3(printk("scsi(%ld): HBA in NL topology.\n",
- ha->host_no));
+ vha->host_no));
ha->current_topology = ISP_CFG_NL;
strcpy(connect_type, "(Loop)");
break;
case 1:
DEBUG3(printk("scsi(%ld): HBA in FL topology.\n",
- ha->host_no));
+ vha->host_no));
ha->switch_cap = sw_cap;
ha->current_topology = ISP_CFG_FL;
strcpy(connect_type, "(FL_Port)");
@@ -1436,7 +1504,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
case 2:
DEBUG3(printk("scsi(%ld): HBA in N P2P topology.\n",
- ha->host_no));
+ vha->host_no));
ha->operating_mode = P2P;
ha->current_topology = ISP_CFG_N;
strcpy(connect_type, "(N_Port-to-N_Port)");
@@ -1444,7 +1512,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
case 3:
DEBUG3(printk("scsi(%ld): HBA in F P2P topology.\n",
- ha->host_no));
+ vha->host_no));
ha->switch_cap = sw_cap;
ha->operating_mode = P2P;
ha->current_topology = ISP_CFG_F;
@@ -1454,7 +1522,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
default:
DEBUG3(printk("scsi(%ld): HBA in unknown topology %x. "
"Using NL.\n",
- ha->host_no, topo));
+ vha->host_no, topo));
ha->current_topology = ISP_CFG_NL;
strcpy(connect_type, "(Loop)");
break;
@@ -1462,29 +1530,31 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
/* Save Host port and loop ID. */
/* byte order - Big Endian */
- ha->d_id.b.domain = domain;
- ha->d_id.b.area = area;
- ha->d_id.b.al_pa = al_pa;
+ vha->d_id.b.domain = domain;
+ vha->d_id.b.area = area;
+ vha->d_id.b.al_pa = al_pa;
- if (!ha->flags.init_done)
+ if (!vha->flags.init_done)
qla_printk(KERN_INFO, ha,
"Topology - %s, Host Loop address 0x%x\n",
- connect_type, ha->loop_id);
+ connect_type, vha->loop_id);
if (rval) {
- DEBUG2_3(printk("scsi(%ld): FAILED.\n", ha->host_no));
+ DEBUG2_3(printk("scsi(%ld): FAILED.\n", vha->host_no));
} else {
- DEBUG3(printk("scsi(%ld): exiting normally.\n", ha->host_no));
+ DEBUG3(printk("scsi(%ld): exiting normally.\n", vha->host_no));
}
return(rval);
}
static inline void
-qla2x00_set_model_info(scsi_qla_host_t *ha, uint8_t *model, size_t len, char *def)
+qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len,
+ char *def)
{
char *st, *en;
uint16_t index;
+ struct qla_hw_data *ha = vha->hw;
if (memcmp(model, BINZERO, len) != 0) {
strncpy(ha->model_number, model, len);
@@ -1516,16 +1586,17 @@ qla2x00_set_model_info(scsi_qla_host_t *ha, uint8_t *model, size_t len, char *de
}
}
if (IS_FWI2_CAPABLE(ha))
- qla2xxx_get_vpd_field(ha, "\x82", ha->model_desc,
+ qla2xxx_get_vpd_field(vha, "\x82", ha->model_desc,
sizeof(ha->model_desc));
}
/* On sparc systems, obtain port and node WWN from firmware
* properties.
*/
-static void qla2xxx_nvram_wwn_from_ofw(scsi_qla_host_t *ha, nvram_t *nv)
+static void qla2xxx_nvram_wwn_from_ofw(scsi_qla_host_t *vha, nvram_t *nv)
{
#ifdef CONFIG_SPARC
+ struct qla_hw_data *ha = vha->hw;
struct pci_dev *pdev = ha->pdev;
struct device_node *dp = pci_device_to_OF_node(pdev);
const u8 *val;
@@ -1555,12 +1626,13 @@ static void qla2xxx_nvram_wwn_from_ofw(scsi_qla_host_t *ha, nvram_t *nv)
* 0 = success.
*/
int
-qla2x00_nvram_config(scsi_qla_host_t *ha)
+qla2x00_nvram_config(scsi_qla_host_t *vha)
{
int rval;
uint8_t chksum = 0;
uint16_t cnt;
uint8_t *dptr1, *dptr2;
+ struct qla_hw_data *ha = vha->hw;
init_cb_t *icb = ha->init_cb;
nvram_t *nv = ha->nvram;
uint8_t *ptr = ha->nvram;
@@ -1576,11 +1648,11 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
ha->nvram_base = 0x80;
/* Get NVRAM data and calculate checksum. */
- ha->isp_ops->read_nvram(ha, ptr, ha->nvram_base, ha->nvram_size);
+ ha->isp_ops->read_nvram(vha, ptr, ha->nvram_base, ha->nvram_size);
for (cnt = 0, chksum = 0; cnt < ha->nvram_size; cnt++)
chksum += *ptr++;
- DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no));
+ DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", vha->host_no));
DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size));
/* Bad NVRAM data, set defaults parameters. */
@@ -1594,7 +1666,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
"invalid -- WWPN) defaults.\n");
if (chksum)
- qla2xxx_hw_event_log(ha, HW_EVENT_NVRAM_CHKSUM_ERR, 0,
+ qla2xxx_hw_event_log(vha, HW_EVENT_NVRAM_CHKSUM_ERR, 0,
MSW(chksum), LSW(chksum));
/*
@@ -1631,7 +1703,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
nv->port_name[3] = 224;
nv->port_name[4] = 139;
- qla2xxx_nvram_wwn_from_ofw(ha, nv);
+ qla2xxx_nvram_wwn_from_ofw(vha, nv);
nv->login_timeout = 4;
@@ -1684,7 +1756,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
strcpy(ha->model_number, "QLA2300");
}
} else {
- qla2x00_set_model_info(ha, nv->model_number,
+ qla2x00_set_model_info(vha, nv->model_number,
sizeof(nv->model_number), "QLA23xx");
}
} else if (IS_QLA2200(ha)) {
@@ -1760,8 +1832,8 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
ha->serial0 = icb->port_name[5];
ha->serial1 = icb->port_name[6];
ha->serial2 = icb->port_name[7];
- ha->node_name = icb->node_name;
- ha->port_name = icb->port_name;
+ memcpy(vha->node_name, icb->node_name, WWN_SIZE);
+ memcpy(vha->port_name, icb->port_name, WWN_SIZE);
icb->execution_throttle = __constant_cpu_to_le16(0xFFFF);
@@ -1829,10 +1901,10 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
icb->response_accumulation_timer = 3;
icb->interrupt_delay_timer = 5;
- ha->flags.process_response_queue = 1;
+ vha->flags.process_response_queue = 1;
} else {
/* Enable ZIO. */
- if (!ha->flags.init_done) {
+ if (!vha->flags.init_done) {
ha->zio_mode = icb->add_firmware_options[0] &
(BIT_3 | BIT_2 | BIT_1 | BIT_0);
ha->zio_timer = icb->interrupt_delay_timer ?
@@ -1840,12 +1912,12 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
}
icb->add_firmware_options[0] &=
~(BIT_3 | BIT_2 | BIT_1 | BIT_0);
- ha->flags.process_response_queue = 0;
+ vha->flags.process_response_queue = 0;
if (ha->zio_mode != QLA_ZIO_DISABLED) {
ha->zio_mode = QLA_ZIO_MODE_6;
DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer "
- "delay (%d us).\n", ha->host_no, ha->zio_mode,
+ "delay (%d us).\n", vha->host_no, ha->zio_mode,
ha->zio_timer * 100));
qla_printk(KERN_INFO, ha,
"ZIO mode %d enabled; timer delay (%d us).\n",
@@ -1853,13 +1925,13 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
icb->add_firmware_options[0] |= (uint8_t)ha->zio_mode;
icb->interrupt_delay_timer = (uint8_t)ha->zio_timer;
- ha->flags.process_response_queue = 1;
+ vha->flags.process_response_queue = 1;
}
}
if (rval) {
DEBUG2_3(printk(KERN_WARNING
- "scsi(%ld): NVRAM configuration failed!\n", ha->host_no));
+ "scsi(%ld): NVRAM configuration failed!\n", vha->host_no));
}
return (rval);
}
@@ -1870,10 +1942,10 @@ qla2x00_rport_del(void *data)
fc_port_t *fcport = data;
struct fc_rport *rport;
- spin_lock_irq(fcport->ha->host->host_lock);
+ spin_lock_irq(fcport->vha->host->host_lock);
rport = fcport->drport;
fcport->drport = NULL;
- spin_unlock_irq(fcport->ha->host->host_lock);
+ spin_unlock_irq(fcport->vha->host->host_lock);
if (rport)
fc_remote_port_delete(rport);
}
@@ -1886,7 +1958,7 @@ qla2x00_rport_del(void *data)
* Returns a pointer to the allocated fcport, or NULL, if none available.
*/
static fc_port_t *
-qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags)
+qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
{
fc_port_t *fcport;
@@ -1895,8 +1967,8 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags)
return NULL;
/* Setup fcport template structure. */
- fcport->ha = ha;
- fcport->vp_idx = ha->vp_idx;
+ fcport->vha = vha;
+ fcport->vp_idx = vha->vp_idx;
fcport->port_type = FCT_UNKNOWN;
fcport->loop_id = FC_NO_LOOP_ID;
atomic_set(&fcport->state, FCS_UNCONFIGURED);
@@ -1919,101 +1991,97 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags)
* 2 = database was full and device was not configured.
*/
static int
-qla2x00_configure_loop(scsi_qla_host_t *ha)
+qla2x00_configure_loop(scsi_qla_host_t *vha)
{
int rval;
unsigned long flags, save_flags;
-
+ struct qla_hw_data *ha = vha->hw;
rval = QLA_SUCCESS;
/* Get Initiator ID */
- if (test_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags)) {
- rval = qla2x00_configure_hba(ha);
+ if (test_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags)) {
+ rval = qla2x00_configure_hba(vha);
if (rval != QLA_SUCCESS) {
DEBUG(printk("scsi(%ld): Unable to configure HBA.\n",
- ha->host_no));
+ vha->host_no));
return (rval);
}
}
- save_flags = flags = ha->dpc_flags;
+ save_flags = flags = vha->dpc_flags;
DEBUG(printk("scsi(%ld): Configure loop -- dpc flags =0x%lx\n",
- ha->host_no, flags));
+ vha->host_no, flags));
/*
* If we have both an RSCN and PORT UPDATE pending then handle them
* both at the same time.
*/
- clear_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
- clear_bit(RSCN_UPDATE, &ha->dpc_flags);
+ clear_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ clear_bit(RSCN_UPDATE, &vha->dpc_flags);
/* Determine what we need to do */
if (ha->current_topology == ISP_CFG_FL &&
(test_bit(LOCAL_LOOP_UPDATE, &flags))) {
- ha->flags.rscn_queue_overflow = 1;
+ vha->flags.rscn_queue_overflow = 1;
set_bit(RSCN_UPDATE, &flags);
} else if (ha->current_topology == ISP_CFG_F &&
(test_bit(LOCAL_LOOP_UPDATE, &flags))) {
- ha->flags.rscn_queue_overflow = 1;
+ vha->flags.rscn_queue_overflow = 1;
set_bit(RSCN_UPDATE, &flags);
clear_bit(LOCAL_LOOP_UPDATE, &flags);
} else if (ha->current_topology == ISP_CFG_N) {
clear_bit(RSCN_UPDATE, &flags);
- } else if (!ha->flags.online ||
+ } else if (!vha->flags.online ||
(test_bit(ABORT_ISP_ACTIVE, &flags))) {
- ha->flags.rscn_queue_overflow = 1;
+ vha->flags.rscn_queue_overflow = 1;
set_bit(RSCN_UPDATE, &flags);
set_bit(LOCAL_LOOP_UPDATE, &flags);
}
if (test_bit(LOCAL_LOOP_UPDATE, &flags)) {
- if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+ if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
rval = QLA_FUNCTION_FAILED;
- } else {
- rval = qla2x00_configure_local_loop(ha);
- }
+ else
+ rval = qla2x00_configure_local_loop(vha);
}
if (rval == QLA_SUCCESS && test_bit(RSCN_UPDATE, &flags)) {
- if (LOOP_TRANSITION(ha)) {
+ if (LOOP_TRANSITION(vha))
rval = QLA_FUNCTION_FAILED;
- } else {
- rval = qla2x00_configure_fabric(ha);
- }
+ else
+ rval = qla2x00_configure_fabric(vha);
}
if (rval == QLA_SUCCESS) {
- if (atomic_read(&ha->loop_down_timer) ||
- test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+ if (atomic_read(&vha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
rval = QLA_FUNCTION_FAILED;
} else {
- atomic_set(&ha->loop_state, LOOP_READY);
+ atomic_set(&vha->loop_state, LOOP_READY);
- DEBUG(printk("scsi(%ld): LOOP READY\n", ha->host_no));
+ DEBUG(printk("scsi(%ld): LOOP READY\n", vha->host_no));
}
}
if (rval) {
DEBUG2_3(printk("%s(%ld): *** FAILED ***\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
} else {
DEBUG3(printk("%s: exiting normally\n", __func__));
}
/* Restore state if a resync event occurred during processing */
- if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+ if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
if (test_bit(LOCAL_LOOP_UPDATE, &save_flags))
- set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
- if (test_bit(RSCN_UPDATE, &save_flags)) {
- ha->flags.rscn_queue_overflow = 1;
- set_bit(RSCN_UPDATE, &ha->dpc_flags);
- }
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ if (test_bit(RSCN_UPDATE, &save_flags))
+ set_bit(RSCN_UPDATE, &vha->dpc_flags);
}
return (rval);
@@ -2032,7 +2100,7 @@ qla2x00_configure_loop(scsi_qla_host_t *ha)
* 0 = success.
*/
static int
-qla2x00_configure_local_loop(scsi_qla_host_t *ha)
+qla2x00_configure_local_loop(scsi_qla_host_t *vha)
{
int rval, rval2;
int found_devs;
@@ -2044,18 +2112,18 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
char *id_iter;
uint16_t loop_id;
uint8_t domain, area, al_pa;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
found_devs = 0;
new_fcport = NULL;
entries = MAX_FIBRE_DEVICES;
- DEBUG3(printk("scsi(%ld): Getting FCAL position map\n", ha->host_no));
- DEBUG3(qla2x00_get_fcal_position_map(ha, NULL));
+ DEBUG3(printk("scsi(%ld): Getting FCAL position map\n", vha->host_no));
+ DEBUG3(qla2x00_get_fcal_position_map(vha, NULL));
/* Get list of logged in devices. */
memset(ha->gid_list, 0, GID_LIST_SIZE);
- rval = qla2x00_get_id_list(ha, ha->gid_list, ha->gid_list_dma,
+ rval = qla2x00_get_id_list(vha, ha->gid_list, ha->gid_list_dma,
&entries);
if (rval != QLA_SUCCESS)
goto cleanup_allocation;
@@ -2066,7 +2134,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
entries * sizeof(struct gid_list_info)));
/* Allocate temporary fcport for any new fcports discovered. */
- new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL);
+ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
if (new_fcport == NULL) {
rval = QLA_MEMORY_ALLOC_FAILED;
goto cleanup_allocation;
@@ -2076,17 +2144,14 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
/*
* Mark local devices that were present with FCF_DEVICE_LOST for now.
*/
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->vp_idx != ha->vp_idx)
- continue;
-
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (atomic_read(&fcport->state) == FCS_ONLINE &&
fcport->port_type != FCT_BROADCAST &&
(fcport->flags & FCF_FABRIC_DEVICE) == 0) {
DEBUG(printk("scsi(%ld): Marking port lost, "
"loop_id=0x%04x\n",
- ha->host_no, fcport->loop_id));
+ vha->host_no, fcport->loop_id));
atomic_set(&fcport->state, FCS_DEVICE_LOST);
fcport->flags &= ~FCF_FARP_DONE;
@@ -2113,7 +2178,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
/* Bypass if not same domain and area of adapter. */
if (area && domain &&
- (area != ha->d_id.b.area || domain != ha->d_id.b.domain))
+ (area != vha->d_id.b.area || domain != vha->d_id.b.domain))
continue;
/* Bypass invalid local loop ID. */
@@ -2125,26 +2190,23 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
new_fcport->d_id.b.area = area;
new_fcport->d_id.b.al_pa = al_pa;
new_fcport->loop_id = loop_id;
- new_fcport->vp_idx = ha->vp_idx;
- rval2 = qla2x00_get_port_database(ha, new_fcport, 0);
+ new_fcport->vp_idx = vha->vp_idx;
+ rval2 = qla2x00_get_port_database(vha, new_fcport, 0);
if (rval2 != QLA_SUCCESS) {
DEBUG2(printk("scsi(%ld): Failed to retrieve fcport "
"information -- get_port_database=%x, "
"loop_id=0x%04x\n",
- ha->host_no, rval2, new_fcport->loop_id));
+ vha->host_no, rval2, new_fcport->loop_id));
DEBUG2(printk("scsi(%ld): Scheduling resync...\n",
- ha->host_no));
- set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+ vha->host_no));
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
continue;
}
/* Check for matching device in port list. */
found = 0;
fcport = NULL;
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->vp_idx != ha->vp_idx)
- continue;
-
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (memcmp(new_fcport->port_name, fcport->port_name,
WWN_SIZE))
continue;
@@ -2164,17 +2226,15 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
if (!found) {
/* New device, add to fcports list. */
new_fcport->flags &= ~FCF_PERSISTENT_BOUND;
- if (ha->parent) {
- new_fcport->ha = ha;
- new_fcport->vp_idx = ha->vp_idx;
- list_add_tail(&new_fcport->vp_fcport,
- &ha->vp_fcports);
+ if (vha->vp_idx) {
+ new_fcport->vha = vha;
+ new_fcport->vp_idx = vha->vp_idx;
}
- list_add_tail(&new_fcport->list, &pha->fcports);
+ list_add_tail(&new_fcport->list, &vha->vp_fcports);
/* Allocate a new replacement fcport. */
fcport = new_fcport;
- new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL);
+ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
if (new_fcport == NULL) {
rval = QLA_MEMORY_ALLOC_FAILED;
goto cleanup_allocation;
@@ -2185,7 +2245,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
/* Base iIDMA settings on HBA port speed. */
fcport->fp_speed = ha->link_data_rate;
- qla2x00_update_fcport(ha, fcport);
+ qla2x00_update_fcport(vha, fcport);
found_devs++;
}
@@ -2195,24 +2255,25 @@ cleanup_allocation:
if (rval != QLA_SUCCESS) {
DEBUG2(printk("scsi(%ld): Configure local loop error exit: "
- "rval=%x\n", ha->host_no, rval));
+ "rval=%x\n", vha->host_no, rval));
}
if (found_devs) {
- ha->device_flags |= DFLG_LOCAL_DEVICES;
- ha->device_flags &= ~DFLG_RETRY_LOCAL_DEVICES;
+ vha->device_flags |= DFLG_LOCAL_DEVICES;
+ vha->device_flags &= ~DFLG_RETRY_LOCAL_DEVICES;
}
return (rval);
}
static void
-qla2x00_iidma_fcport(scsi_qla_host_t *ha, fc_port_t *fcport)
+qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
{
#define LS_UNKNOWN 2
static char *link_speeds[5] = { "1", "2", "?", "4", "8" };
int rval;
uint16_t mb[6];
+ struct qla_hw_data *ha = vha->hw;
if (!IS_IIDMA_CAPABLE(ha))
return;
@@ -2221,12 +2282,12 @@ qla2x00_iidma_fcport(scsi_qla_host_t *ha, fc_port_t *fcport)
fcport->fp_speed > ha->link_data_rate)
return;
- rval = qla2x00_set_idma_speed(ha, fcport->loop_id, fcport->fp_speed,
+ rval = qla2x00_set_idma_speed(vha, fcport->loop_id, fcport->fp_speed,
mb);
if (rval != QLA_SUCCESS) {
DEBUG2(printk("scsi(%ld): Unable to adjust iIDMA "
"%02x%02x%02x%02x%02x%02x%02x%02x -- %04x %x %04x %04x.\n",
- ha->host_no, fcport->port_name[0], fcport->port_name[1],
+ vha->host_no, fcport->port_name[0], fcport->port_name[1],
fcport->port_name[2], fcport->port_name[3],
fcport->port_name[4], fcport->port_name[5],
fcport->port_name[6], fcport->port_name[7], rval,
@@ -2244,10 +2305,11 @@ qla2x00_iidma_fcport(scsi_qla_host_t *ha, fc_port_t *fcport)
}
static void
-qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport)
+qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport)
{
struct fc_rport_identifiers rport_ids;
struct fc_rport *rport;
+ struct qla_hw_data *ha = vha->hw;
if (fcport->drport)
qla2x00_rport_del(fcport);
@@ -2257,15 +2319,15 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport)
rport_ids.port_id = fcport->d_id.b.domain << 16 |
fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa;
rport_ids.roles = FC_RPORT_ROLE_UNKNOWN;
- fcport->rport = rport = fc_remote_port_add(ha->host, 0, &rport_ids);
+ fcport->rport = rport = fc_remote_port_add(vha->host, 0, &rport_ids);
if (!rport) {
qla_printk(KERN_WARNING, ha,
"Unable to allocate fc remote port!\n");
return;
}
- spin_lock_irq(fcport->ha->host->host_lock);
+ spin_lock_irq(fcport->vha->host->host_lock);
*((fc_port_t **)rport->dd_data) = fcport;
- spin_unlock_irq(fcport->ha->host->host_lock);
+ spin_unlock_irq(fcport->vha->host->host_lock);
rport->supported_classes = fcport->supported_classes;
@@ -2293,23 +2355,23 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport)
* Kernel context.
*/
void
-qla2x00_update_fcport(scsi_qla_host_t *ha, fc_port_t *fcport)
+qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
{
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
- fcport->ha = ha;
+ fcport->vha = vha;
fcport->login_retry = 0;
- fcport->port_login_retry_count = pha->port_down_retry_count *
+ fcport->port_login_retry_count = ha->port_down_retry_count *
PORT_RETRY_TIME;
- atomic_set(&fcport->port_down_timer, pha->port_down_retry_count *
+ atomic_set(&fcport->port_down_timer, ha->port_down_retry_count *
PORT_RETRY_TIME);
fcport->flags &= ~FCF_LOGIN_NEEDED;
- qla2x00_iidma_fcport(ha, fcport);
+ qla2x00_iidma_fcport(vha, fcport);
atomic_set(&fcport->state, FCS_ONLINE);
- qla2x00_reg_remote_port(ha, fcport);
+ qla2x00_reg_remote_port(vha, fcport);
}
/*
@@ -2324,7 +2386,7 @@ qla2x00_update_fcport(scsi_qla_host_t *ha, fc_port_t *fcport)
* BIT_0 = error
*/
static int
-qla2x00_configure_fabric(scsi_qla_host_t *ha)
+qla2x00_configure_fabric(scsi_qla_host_t *vha)
{
int rval, rval2;
fc_port_t *fcport, *fcptemp;
@@ -2332,25 +2394,26 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
uint16_t mb[MAILBOX_REGISTER_COUNT];
uint16_t loop_id;
LIST_HEAD(new_fcports);
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
/* If FL port exists, then SNS is present */
if (IS_FWI2_CAPABLE(ha))
loop_id = NPH_F_PORT;
else
loop_id = SNS_FL_PORT;
- rval = qla2x00_get_port_name(ha, loop_id, ha->fabric_node_name, 1);
+ rval = qla2x00_get_port_name(vha, loop_id, vha->fabric_node_name, 1);
if (rval != QLA_SUCCESS) {
DEBUG2(printk("scsi(%ld): MBC_GET_PORT_NAME Failed, No FL "
- "Port\n", ha->host_no));
+ "Port\n", vha->host_no));
- ha->device_flags &= ~SWITCH_FOUND;
+ vha->device_flags &= ~SWITCH_FOUND;
return (QLA_SUCCESS);
}
- ha->device_flags |= SWITCH_FOUND;
+ vha->device_flags |= SWITCH_FOUND;
/* Mark devices that need re-synchronization. */
- rval2 = qla2x00_device_resync(ha);
+ rval2 = qla2x00_device_resync(vha);
if (rval2 == QLA_RSCNS_HANDLED) {
/* No point doing the scan, just continue. */
return (QLA_SUCCESS);
@@ -2358,15 +2421,15 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
do {
/* FDMI support. */
if (ql2xfdmienable &&
- test_and_clear_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags))
- qla2x00_fdmi_register(ha);
+ test_and_clear_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags))
+ qla2x00_fdmi_register(vha);
/* Ensure we are logged into the SNS. */
if (IS_FWI2_CAPABLE(ha))
loop_id = NPH_SNS;
else
loop_id = SIMPLE_NAME_SERVER;
- ha->isp_ops->fabric_login(ha, loop_id, 0xff, 0xff,
+ ha->isp_ops->fabric_login(vha, loop_id, 0xff, 0xff,
0xfc, mb, BIT_1 | BIT_0);
if (mb[0] != MBS_COMMAND_COMPLETE) {
DEBUG2(qla_printk(KERN_INFO, ha,
@@ -2376,29 +2439,29 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
return (QLA_SUCCESS);
}
- if (test_and_clear_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags)) {
- if (qla2x00_rft_id(ha)) {
+ if (test_and_clear_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags)) {
+ if (qla2x00_rft_id(vha)) {
/* EMPTY */
DEBUG2(printk("scsi(%ld): Register FC-4 "
- "TYPE failed.\n", ha->host_no));
+ "TYPE failed.\n", vha->host_no));
}
- if (qla2x00_rff_id(ha)) {
+ if (qla2x00_rff_id(vha)) {
/* EMPTY */
DEBUG2(printk("scsi(%ld): Register FC-4 "
- "Features failed.\n", ha->host_no));
+ "Features failed.\n", vha->host_no));
}
- if (qla2x00_rnn_id(ha)) {
+ if (qla2x00_rnn_id(vha)) {
/* EMPTY */
DEBUG2(printk("scsi(%ld): Register Node Name "
- "failed.\n", ha->host_no));
- } else if (qla2x00_rsnn_nn(ha)) {
+ "failed.\n", vha->host_no));
+ } else if (qla2x00_rsnn_nn(vha)) {
/* EMPTY */
DEBUG2(printk("scsi(%ld): Register Symbolic "
- "Node Name failed.\n", ha->host_no));
+ "Node Name failed.\n", vha->host_no));
}
}
- rval = qla2x00_find_all_fabric_devs(ha, &new_fcports);
+ rval = qla2x00_find_all_fabric_devs(vha, &new_fcports);
if (rval != QLA_SUCCESS)
break;
@@ -2406,24 +2469,21 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
* Logout all previous fabric devices marked lost, except
* tape devices.
*/
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->vp_idx !=ha->vp_idx)
- continue;
-
- if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break;
if ((fcport->flags & FCF_FABRIC_DEVICE) == 0)
continue;
if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) {
- qla2x00_mark_device_lost(ha, fcport,
+ qla2x00_mark_device_lost(vha, fcport,
ql2xplogiabsentdevice, 0);
if (fcport->loop_id != FC_NO_LOOP_ID &&
(fcport->flags & FCF_TAPE_PRESENT) == 0 &&
fcport->port_type != FCT_INITIATOR &&
fcport->port_type != FCT_BROADCAST) {
- ha->isp_ops->fabric_logout(ha,
+ ha->isp_ops->fabric_logout(vha,
fcport->loop_id,
fcport->d_id.b.domain,
fcport->d_id.b.area,
@@ -2434,18 +2494,15 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
}
/* Starting free loop ID. */
- next_loopid = pha->min_external_loopid;
+ next_loopid = ha->min_external_loopid;
/*
* Scan through our port list and login entries that need to be
* logged in.
*/
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->vp_idx != ha->vp_idx)
- continue;
-
- if (atomic_read(&ha->loop_down_timer) ||
- test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (atomic_read(&vha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break;
if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
@@ -2455,14 +2512,14 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
if (fcport->loop_id == FC_NO_LOOP_ID) {
fcport->loop_id = next_loopid;
rval = qla2x00_find_new_loop_id(
- to_qla_parent(ha), fcport);
+ base_vha, fcport);
if (rval != QLA_SUCCESS) {
/* Ran out of IDs to use */
break;
}
}
/* Login and update database */
- qla2x00_fabric_dev_login(ha, fcport, &next_loopid);
+ qla2x00_fabric_dev_login(vha, fcport, &next_loopid);
}
/* Exit if out of loop IDs. */
@@ -2474,31 +2531,26 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
* Login and add the new devices to our port list.
*/
list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) {
- if (atomic_read(&ha->loop_down_timer) ||
- test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+ if (atomic_read(&vha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break;
/* Find a new loop ID to use. */
fcport->loop_id = next_loopid;
- rval = qla2x00_find_new_loop_id(to_qla_parent(ha),
- fcport);
+ rval = qla2x00_find_new_loop_id(base_vha, fcport);
if (rval != QLA_SUCCESS) {
/* Ran out of IDs to use */
break;
}
/* Login and update database */
- qla2x00_fabric_dev_login(ha, fcport, &next_loopid);
-
- if (ha->parent) {
- fcport->ha = ha;
- fcport->vp_idx = ha->vp_idx;
- list_add_tail(&fcport->vp_fcport,
- &ha->vp_fcports);
- list_move_tail(&fcport->list,
- &ha->parent->fcports);
- } else
- list_move_tail(&fcport->list, &ha->fcports);
+ qla2x00_fabric_dev_login(vha, fcport, &next_loopid);
+
+ if (vha->vp_idx) {
+ fcport->vha = vha;
+ fcport->vp_idx = vha->vp_idx;
+ }
+ list_move_tail(&fcport->list, &vha->vp_fcports);
}
} while (0);
@@ -2510,7 +2562,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
if (rval) {
DEBUG2(printk("scsi(%ld): Configure fabric error exit: "
- "rval=%d\n", ha->host_no, rval));
+ "rval=%d\n", vha->host_no, rval));
}
return (rval);
@@ -2531,7 +2583,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
* Kernel context.
*/
static int
-qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
+qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
+ struct list_head *new_fcports)
{
int rval;
uint16_t loop_id;
@@ -2542,11 +2595,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
int swl_idx;
int first_dev, last_dev;
port_id_t wrap, nxt_d_id;
- int vp_index;
- int empty_vp_index;
- int found_vp;
- scsi_qla_host_t *vha;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *vp, *base_vha = pci_get_drvdata(ha->pdev);
rval = QLA_SUCCESS;
@@ -2555,43 +2605,42 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
if (!swl) {
/*EMPTY*/
DEBUG2(printk("scsi(%ld): GID_PT allocations failed, fallback "
- "on GA_NXT\n", ha->host_no));
+ "on GA_NXT\n", vha->host_no));
} else {
- if (qla2x00_gid_pt(ha, swl) != QLA_SUCCESS) {
+ if (qla2x00_gid_pt(vha, swl) != QLA_SUCCESS) {
kfree(swl);
swl = NULL;
- } else if (qla2x00_gpn_id(ha, swl) != QLA_SUCCESS) {
+ } else if (qla2x00_gpn_id(vha, swl) != QLA_SUCCESS) {
kfree(swl);
swl = NULL;
- } else if (qla2x00_gnn_id(ha, swl) != QLA_SUCCESS) {
+ } else if (qla2x00_gnn_id(vha, swl) != QLA_SUCCESS) {
kfree(swl);
swl = NULL;
} else if (ql2xiidmaenable &&
- qla2x00_gfpn_id(ha, swl) == QLA_SUCCESS) {
- qla2x00_gpsc(ha, swl);
+ qla2x00_gfpn_id(vha, swl) == QLA_SUCCESS) {
+ qla2x00_gpsc(vha, swl);
}
}
swl_idx = 0;
/* Allocate temporary fcport for any new fcports discovered. */
- new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL);
+ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
if (new_fcport == NULL) {
kfree(swl);
return (QLA_MEMORY_ALLOC_FAILED);
}
new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED);
- new_fcport->vp_idx = ha->vp_idx;
/* Set start port ID scan at adapter ID. */
first_dev = 1;
last_dev = 0;
/* Starting free loop ID. */
- loop_id = pha->min_external_loopid;
- for (; loop_id <= ha->last_loop_id; loop_id++) {
- if (qla2x00_is_reserved_id(ha, loop_id))
+ loop_id = ha->min_external_loopid;
+ for (; loop_id <= ha->max_loop_id; loop_id++) {
+ if (qla2x00_is_reserved_id(vha, loop_id))
continue;
- if (atomic_read(&ha->loop_down_timer) || LOOP_TRANSITION(ha))
+ if (atomic_read(&vha->loop_down_timer) || LOOP_TRANSITION(vha))
break;
if (swl != NULL) {
@@ -2614,7 +2663,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
}
} else {
/* Send GA_NXT to the switch */
- rval = qla2x00_ga_nxt(ha, new_fcport);
+ rval = qla2x00_ga_nxt(vha, new_fcport);
if (rval != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"SNS scan failed -- assuming zero-entry "
@@ -2635,44 +2684,31 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
first_dev = 0;
} else if (new_fcport->d_id.b24 == wrap.b24) {
DEBUG2(printk("scsi(%ld): device wrap (%02x%02x%02x)\n",
- ha->host_no, new_fcport->d_id.b.domain,
+ vha->host_no, new_fcport->d_id.b.domain,
new_fcport->d_id.b.area, new_fcport->d_id.b.al_pa));
break;
}
/* Bypass if same physical adapter. */
- if (new_fcport->d_id.b24 == pha->d_id.b24)
+ if (new_fcport->d_id.b24 == base_vha->d_id.b24)
continue;
/* Bypass virtual ports of the same host. */
- if (pha->num_vhosts) {
- for_each_mapped_vp_idx(pha, vp_index) {
- empty_vp_index = 1;
- found_vp = 0;
- list_for_each_entry(vha, &pha->vp_list,
- vp_list) {
- if (vp_index == vha->vp_idx) {
- empty_vp_index = 0;
- found_vp = 1;
- break;
- }
- }
-
- if (empty_vp_index)
- continue;
-
- if (found_vp &&
- new_fcport->d_id.b24 == vha->d_id.b24)
+ found = 0;
+ if (ha->num_vhosts) {
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ if (new_fcport->d_id.b24 == vp->d_id.b24) {
+ found = 1;
break;
+ }
}
-
- if (vp_index <= pha->max_npiv_vports)
+ if (found)
continue;
}
/* Bypass if same domain and area of adapter. */
if (((new_fcport->d_id.b24 & 0xffff00) ==
- (ha->d_id.b24 & 0xffff00)) && ha->current_topology ==
+ (vha->d_id.b24 & 0xffff00)) && ha->current_topology ==
ISP_CFG_FL)
continue;
@@ -2682,9 +2718,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
/* Locate matching device in database. */
found = 0;
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (new_fcport->vp_idx != fcport->vp_idx)
- continue;
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (memcmp(new_fcport->port_name, fcport->port_name,
WWN_SIZE))
continue;
@@ -2728,7 +2762,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
(fcport->flags & FCF_TAPE_PRESENT) == 0 &&
fcport->port_type != FCT_INITIATOR &&
fcport->port_type != FCT_BROADCAST) {
- ha->isp_ops->fabric_logout(ha, fcport->loop_id,
+ ha->isp_ops->fabric_logout(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa);
fcport->loop_id = FC_NO_LOOP_ID;
@@ -2739,27 +2773,25 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
if (found)
continue;
-
/* If device was not in our fcports list, then add it. */
list_add_tail(&new_fcport->list, new_fcports);
/* Allocate a new replacement fcport. */
nxt_d_id.b24 = new_fcport->d_id.b24;
- new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL);
+ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
if (new_fcport == NULL) {
kfree(swl);
return (QLA_MEMORY_ALLOC_FAILED);
}
new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED);
new_fcport->d_id.b24 = nxt_d_id.b24;
- new_fcport->vp_idx = ha->vp_idx;
}
kfree(swl);
kfree(new_fcport);
if (!list_empty(new_fcports))
- ha->device_flags |= DFLG_FABRIC_DEVICES;
+ vha->device_flags |= DFLG_FABRIC_DEVICES;
return (rval);
}
@@ -2779,13 +2811,14 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
* Kernel context.
*/
static int
-qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev)
+qla2x00_find_new_loop_id(scsi_qla_host_t *vha, fc_port_t *dev)
{
int rval;
int found;
fc_port_t *fcport;
uint16_t first_loop_id;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *vp;
rval = QLA_SUCCESS;
@@ -2794,17 +2827,15 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev)
for (;;) {
/* Skip loop ID if already used by adapter. */
- if (dev->loop_id == ha->loop_id) {
+ if (dev->loop_id == vha->loop_id)
dev->loop_id++;
- }
/* Skip reserved loop IDs. */
- while (qla2x00_is_reserved_id(ha, dev->loop_id)) {
+ while (qla2x00_is_reserved_id(vha, dev->loop_id))
dev->loop_id++;
- }
/* Reset loop ID if passed the end. */
- if (dev->loop_id > ha->last_loop_id) {
+ if (dev->loop_id > ha->max_loop_id) {
/* first loop ID. */
dev->loop_id = ha->min_external_loopid;
}
@@ -2812,12 +2843,17 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev)
/* Check for loop ID being already in use. */
found = 0;
fcport = NULL;
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->loop_id == dev->loop_id && fcport != dev) {
- /* ID possibly in use */
- found++;
- break;
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ list_for_each_entry(fcport, &vp->vp_fcports, list) {
+ if (fcport->loop_id == dev->loop_id &&
+ fcport != dev) {
+ /* ID possibly in use */
+ found++;
+ break;
+ }
}
+ if (found)
+ break;
}
/* If not in use then it is free to use. */
@@ -2850,7 +2886,7 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev)
* Kernel context.
*/
static int
-qla2x00_device_resync(scsi_qla_host_t *ha)
+qla2x00_device_resync(scsi_qla_host_t *vha)
{
int rval;
uint32_t mask;
@@ -2859,14 +2895,13 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
uint8_t rscn_out_iter;
uint8_t format;
port_id_t d_id;
- scsi_qla_host_t *pha = to_qla_parent(ha);
rval = QLA_RSCNS_HANDLED;
- while (ha->rscn_out_ptr != ha->rscn_in_ptr ||
- ha->flags.rscn_queue_overflow) {
+ while (vha->rscn_out_ptr != vha->rscn_in_ptr ||
+ vha->flags.rscn_queue_overflow) {
- rscn_entry = ha->rscn_queue[ha->rscn_out_ptr];
+ rscn_entry = vha->rscn_queue[vha->rscn_out_ptr];
format = MSB(MSW(rscn_entry));
d_id.b.domain = LSB(MSW(rscn_entry));
d_id.b.area = MSB(LSW(rscn_entry));
@@ -2874,37 +2909,37 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
DEBUG(printk("scsi(%ld): RSCN queue entry[%d] = "
"[%02x/%02x%02x%02x].\n",
- ha->host_no, ha->rscn_out_ptr, format, d_id.b.domain,
+ vha->host_no, vha->rscn_out_ptr, format, d_id.b.domain,
d_id.b.area, d_id.b.al_pa));
- ha->rscn_out_ptr++;
- if (ha->rscn_out_ptr == MAX_RSCN_COUNT)
- ha->rscn_out_ptr = 0;
+ vha->rscn_out_ptr++;
+ if (vha->rscn_out_ptr == MAX_RSCN_COUNT)
+ vha->rscn_out_ptr = 0;
/* Skip duplicate entries. */
- for (rscn_out_iter = ha->rscn_out_ptr;
- !ha->flags.rscn_queue_overflow &&
- rscn_out_iter != ha->rscn_in_ptr;
+ for (rscn_out_iter = vha->rscn_out_ptr;
+ !vha->flags.rscn_queue_overflow &&
+ rscn_out_iter != vha->rscn_in_ptr;
rscn_out_iter = (rscn_out_iter ==
(MAX_RSCN_COUNT - 1)) ? 0: rscn_out_iter + 1) {
- if (rscn_entry != ha->rscn_queue[rscn_out_iter])
+ if (rscn_entry != vha->rscn_queue[rscn_out_iter])
break;
DEBUG(printk("scsi(%ld): Skipping duplicate RSCN queue "
- "entry found at [%d].\n", ha->host_no,
+ "entry found at [%d].\n", vha->host_no,
rscn_out_iter));
- ha->rscn_out_ptr = rscn_out_iter;
+ vha->rscn_out_ptr = rscn_out_iter;
}
/* Queue overflow, set switch default case. */
- if (ha->flags.rscn_queue_overflow) {
+ if (vha->flags.rscn_queue_overflow) {
DEBUG(printk("scsi(%ld): device_resync: rscn "
- "overflow.\n", ha->host_no));
+ "overflow.\n", vha->host_no));
format = 3;
- ha->flags.rscn_queue_overflow = 0;
+ vha->flags.rscn_queue_overflow = 0;
}
switch (format) {
@@ -2920,16 +2955,13 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
default:
mask = 0x0;
d_id.b24 = 0;
- ha->rscn_out_ptr = ha->rscn_in_ptr;
+ vha->rscn_out_ptr = vha->rscn_in_ptr;
break;
}
rval = QLA_SUCCESS;
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->vp_idx != ha->vp_idx)
- continue;
-
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
(fcport->d_id.b24 & mask) != d_id.b24 ||
fcport->port_type == FCT_BROADCAST)
@@ -2938,7 +2970,7 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
if (atomic_read(&fcport->state) == FCS_ONLINE) {
if (format != 3 ||
fcport->port_type != FCT_INITIATOR) {
- qla2x00_mark_device_lost(ha, fcport,
+ qla2x00_mark_device_lost(vha, fcport,
0, 0);
}
}
@@ -2965,30 +2997,31 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
* Kernel context.
*/
static int
-qla2x00_fabric_dev_login(scsi_qla_host_t *ha, fc_port_t *fcport,
+qla2x00_fabric_dev_login(scsi_qla_host_t *vha, fc_port_t *fcport,
uint16_t *next_loopid)
{
int rval;
int retry;
uint8_t opts;
+ struct qla_hw_data *ha = vha->hw;
rval = QLA_SUCCESS;
retry = 0;
- rval = qla2x00_fabric_login(ha, fcport, next_loopid);
+ rval = qla2x00_fabric_login(vha, fcport, next_loopid);
if (rval == QLA_SUCCESS) {
/* Send an ADISC to tape devices.*/
opts = 0;
if (fcport->flags & FCF_TAPE_PRESENT)
opts |= BIT_1;
- rval = qla2x00_get_port_database(ha, fcport, opts);
+ rval = qla2x00_get_port_database(vha, fcport, opts);
if (rval != QLA_SUCCESS) {
- ha->isp_ops->fabric_logout(ha, fcport->loop_id,
+ ha->isp_ops->fabric_logout(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa);
- qla2x00_mark_device_lost(ha, fcport, 1, 0);
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
} else {
- qla2x00_update_fcport(ha, fcport);
+ qla2x00_update_fcport(vha, fcport);
}
}
@@ -3010,13 +3043,14 @@ qla2x00_fabric_dev_login(scsi_qla_host_t *ha, fc_port_t *fcport,
* 3 - Fatal error
*/
int
-qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
+qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport,
uint16_t *next_loopid)
{
int rval;
int retry;
uint16_t tmp_loopid;
uint16_t mb[MAILBOX_REGISTER_COUNT];
+ struct qla_hw_data *ha = vha->hw;
retry = 0;
tmp_loopid = 0;
@@ -3024,11 +3058,11 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
for (;;) {
DEBUG(printk("scsi(%ld): Trying Fabric Login w/loop id 0x%04x "
"for port %02x%02x%02x.\n",
- ha->host_no, fcport->loop_id, fcport->d_id.b.domain,
+ vha->host_no, fcport->loop_id, fcport->d_id.b.domain,
fcport->d_id.b.area, fcport->d_id.b.al_pa));
/* Login fcport on switch. */
- ha->isp_ops->fabric_login(ha, fcport->loop_id,
+ ha->isp_ops->fabric_login(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa, mb, BIT_0);
if (mb[0] == MBS_PORT_ID_USED) {
@@ -3084,7 +3118,7 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
* Loop ID already used, try next loop ID.
*/
fcport->loop_id++;
- rval = qla2x00_find_new_loop_id(ha, fcport);
+ rval = qla2x00_find_new_loop_id(vha, fcport);
if (rval != QLA_SUCCESS) {
/* Ran out of loop IDs to use */
break;
@@ -3096,10 +3130,10 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
* dead.
*/
*next_loopid = fcport->loop_id;
- ha->isp_ops->fabric_logout(ha, fcport->loop_id,
+ ha->isp_ops->fabric_logout(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa);
- qla2x00_mark_device_lost(ha, fcport, 1, 0);
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
rval = 1;
break;
@@ -3109,12 +3143,12 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
*/
DEBUG2(printk("%s(%ld): failed=%x port_id=%02x%02x%02x "
"loop_id=%x jiffies=%lx.\n",
- __func__, ha->host_no, mb[0],
+ __func__, vha->host_no, mb[0],
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa, fcport->loop_id, jiffies));
*next_loopid = fcport->loop_id;
- ha->isp_ops->fabric_logout(ha, fcport->loop_id,
+ ha->isp_ops->fabric_logout(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa);
fcport->loop_id = FC_NO_LOOP_ID;
@@ -3142,13 +3176,13 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
* 3 - Fatal error
*/
int
-qla2x00_local_device_login(scsi_qla_host_t *ha, fc_port_t *fcport)
+qla2x00_local_device_login(scsi_qla_host_t *vha, fc_port_t *fcport)
{
int rval;
uint16_t mb[MAILBOX_REGISTER_COUNT];
memset(mb, 0, sizeof(mb));
- rval = qla2x00_login_local_device(ha, fcport, mb, BIT_0);
+ rval = qla2x00_login_local_device(vha, fcport, mb, BIT_0);
if (rval == QLA_SUCCESS) {
/* Interrogate mailbox registers for any errors */
if (mb[0] == MBS_COMMAND_ERROR)
@@ -3172,57 +3206,57 @@ qla2x00_local_device_login(scsi_qla_host_t *ha, fc_port_t *fcport)
* 0 = success
*/
int
-qla2x00_loop_resync(scsi_qla_host_t *ha)
+qla2x00_loop_resync(scsi_qla_host_t *vha)
{
- int rval;
+ int rval = QLA_SUCCESS;
uint32_t wait_time;
-
- rval = QLA_SUCCESS;
-
- atomic_set(&ha->loop_state, LOOP_UPDATE);
- clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags);
- if (ha->flags.online) {
- if (!(rval = qla2x00_fw_ready(ha))) {
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
+
+ atomic_set(&vha->loop_state, LOOP_UPDATE);
+ clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
+ if (vha->flags.online) {
+ if (!(rval = qla2x00_fw_ready(vha))) {
/* Wait at most MAX_TARGET RSCNs for a stable link. */
wait_time = 256;
do {
- atomic_set(&ha->loop_state, LOOP_UPDATE);
+ atomic_set(&vha->loop_state, LOOP_UPDATE);
/* Issue a marker after FW becomes ready. */
- qla2x00_marker(ha, 0, 0, MK_SYNC_ALL);
- ha->marker_needed = 0;
+ qla2x00_marker(vha, req, rsp, 0, 0,
+ MK_SYNC_ALL);
+ vha->marker_needed = 0;
/* Remap devices on Loop. */
- clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+ clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
- qla2x00_configure_loop(ha);
+ qla2x00_configure_loop(vha);
wait_time--;
- } while (!atomic_read(&ha->loop_down_timer) &&
- !(test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) &&
- wait_time &&
- (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)));
+ } while (!atomic_read(&vha->loop_down_timer) &&
+ !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
+ && wait_time && (test_bit(LOOP_RESYNC_NEEDED,
+ &vha->dpc_flags)));
}
}
- if (test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) {
+ if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
return (QLA_FUNCTION_FAILED);
- }
- if (rval) {
+ if (rval)
DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__));
- }
return (rval);
}
void
-qla2x00_update_fcports(scsi_qla_host_t *ha)
+qla2x00_update_fcports(scsi_qla_host_t *vha)
{
fc_port_t *fcport;
/* Go with deferred removal of rport references. */
- list_for_each_entry(fcport, &ha->fcports, list)
- if (fcport->drport &&
+ list_for_each_entry(fcport, &vha->vp_fcports, list)
+ if (fcport && fcport->drport &&
atomic_read(&fcport->state) != FCS_UNCONFIGURED)
qla2x00_rport_del(fcport);
}
@@ -3238,63 +3272,65 @@ qla2x00_update_fcports(scsi_qla_host_t *ha)
* 0 = success
*/
int
-qla2x00_abort_isp(scsi_qla_host_t *ha)
+qla2x00_abort_isp(scsi_qla_host_t *vha)
{
int rval;
uint8_t status = 0;
- scsi_qla_host_t *vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *vp;
+ struct req_que *req = ha->req_q_map[0];
- if (ha->flags.online) {
- ha->flags.online = 0;
- clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ if (vha->flags.online) {
+ vha->flags.online = 0;
+ clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
ha->qla_stats.total_isp_aborts++;
qla_printk(KERN_INFO, ha,
"Performing ISP error recovery - ha= %p.\n", ha);
- ha->isp_ops->reset_chip(ha);
-
- atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
- if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
- atomic_set(&ha->loop_state, LOOP_DOWN);
- qla2x00_mark_all_devices_lost(ha, 0);
- list_for_each_entry(vha, &ha->vp_list, vp_list)
- qla2x00_mark_all_devices_lost(vha, 0);
+ ha->isp_ops->reset_chip(vha);
+
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ qla2x00_mark_all_devices_lost(vha, 0);
+ list_for_each_entry(vp, &ha->vp_list, list)
+ qla2x00_mark_all_devices_lost(vp, 0);
} else {
- if (!atomic_read(&ha->loop_down_timer))
- atomic_set(&ha->loop_down_timer,
+ if (!atomic_read(&vha->loop_down_timer))
+ atomic_set(&vha->loop_down_timer,
LOOP_DOWN_TIME);
}
/* Requeue all commands in outstanding command list. */
- qla2x00_abort_all_cmds(ha, DID_RESET << 16);
+ qla2x00_abort_all_cmds(vha, DID_RESET << 16);
- ha->isp_ops->get_flash_version(ha, ha->request_ring);
+ ha->isp_ops->get_flash_version(vha, req->ring);
- ha->isp_ops->nvram_config(ha);
+ ha->isp_ops->nvram_config(vha);
- if (!qla2x00_restart_isp(ha)) {
- clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+ if (!qla2x00_restart_isp(vha)) {
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
- if (!atomic_read(&ha->loop_down_timer)) {
+ if (!atomic_read(&vha->loop_down_timer)) {
/*
* Issue marker command only when we are going
* to start the I/O .
*/
- ha->marker_needed = 1;
+ vha->marker_needed = 1;
}
- ha->flags.online = 1;
+ vha->flags.online = 1;
ha->isp_ops->enable_intrs(ha);
ha->isp_abort_cnt = 0;
- clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags);
+ clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
if (ha->fce) {
ha->flags.fce_enabled = 1;
memset(ha->fce, 0,
fce_calc_size(ha->fce_bufs));
- rval = qla2x00_enable_fce_trace(ha,
+ rval = qla2x00_enable_fce_trace(vha,
ha->fce_dma, ha->fce_bufs, ha->fce_mb,
&ha->fce_bufs);
if (rval) {
@@ -3307,7 +3343,7 @@ qla2x00_abort_isp(scsi_qla_host_t *ha)
if (ha->eft) {
memset(ha->eft, 0, EFT_SIZE);
- rval = qla2x00_enable_eft_trace(ha,
+ rval = qla2x00_enable_eft_trace(vha,
ha->eft_dma, EFT_NUM_BUFFERS);
if (rval) {
qla_printk(KERN_WARNING, ha,
@@ -3316,8 +3352,8 @@ qla2x00_abort_isp(scsi_qla_host_t *ha)
}
}
} else { /* failed the ISP abort */
- ha->flags.online = 1;
- if (test_bit(ISP_ABORT_RETRY, &ha->dpc_flags)) {
+ vha->flags.online = 1;
+ if (test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
if (ha->isp_abort_cnt == 0) {
qla_printk(KERN_WARNING, ha,
"ISP error recovery failed - "
@@ -3326,37 +3362,41 @@ qla2x00_abort_isp(scsi_qla_host_t *ha)
* The next call disables the board
* completely.
*/
- ha->isp_ops->reset_adapter(ha);
- ha->flags.online = 0;
+ ha->isp_ops->reset_adapter(vha);
+ vha->flags.online = 0;
clear_bit(ISP_ABORT_RETRY,
- &ha->dpc_flags);
+ &vha->dpc_flags);
status = 0;
} else { /* schedule another ISP abort */
ha->isp_abort_cnt--;
DEBUG(printk("qla%ld: ISP abort - "
"retry remaining %d\n",
- ha->host_no, ha->isp_abort_cnt));
+ vha->host_no, ha->isp_abort_cnt));
status = 1;
}
} else {
ha->isp_abort_cnt = MAX_RETRIES_OF_ISP_ABORT;
DEBUG(printk("qla2x00(%ld): ISP error recovery "
"- retrying (%d) more times\n",
- ha->host_no, ha->isp_abort_cnt));
- set_bit(ISP_ABORT_RETRY, &ha->dpc_flags);
+ vha->host_no, ha->isp_abort_cnt));
+ set_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
status = 1;
}
}
}
- if (status) {
+ if (!status) {
+ DEBUG(printk(KERN_INFO
+ "qla2x00_abort_isp(%ld): succeeded.\n",
+ vha->host_no));
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ if (vp->vp_idx)
+ qla2x00_vp_abort_isp(vp);
+ }
+ } else {
qla_printk(KERN_INFO, ha,
"qla2x00_abort_isp: **** FAILED ****\n");
- } else {
- DEBUG(printk(KERN_INFO
- "qla2x00_abort_isp(%ld): exiting.\n",
- ha->host_no));
}
return(status);
@@ -3373,42 +3413,50 @@ qla2x00_abort_isp(scsi_qla_host_t *ha)
* 0 = success
*/
static int
-qla2x00_restart_isp(scsi_qla_host_t *ha)
+qla2x00_restart_isp(scsi_qla_host_t *vha)
{
uint8_t status = 0;
uint32_t wait_time;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
/* If firmware needs to be loaded */
- if (qla2x00_isp_firmware(ha)) {
- ha->flags.online = 0;
- if (!(status = ha->isp_ops->chip_diag(ha)))
- status = qla2x00_setup_chip(ha);
+ if (qla2x00_isp_firmware(vha)) {
+ vha->flags.online = 0;
+ status = ha->isp_ops->chip_diag(vha);
+ if (!status)
+ status = qla2x00_setup_chip(vha);
}
- if (!status && !(status = qla2x00_init_rings(ha))) {
- clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
- if (!(status = qla2x00_fw_ready(ha))) {
+ if (!status && !(status = qla2x00_init_rings(vha))) {
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+ /* Initialize the queues in use */
+ qla25xx_init_queues(ha);
+
+ status = qla2x00_fw_ready(vha);
+ if (!status) {
DEBUG(printk("%s(): Start configure loop, "
"status = %d\n", __func__, status));
/* Issue a marker after FW becomes ready. */
- qla2x00_marker(ha, 0, 0, MK_SYNC_ALL);
+ qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
- ha->flags.online = 1;
+ vha->flags.online = 1;
/* Wait at most MAX_TARGET RSCNs for a stable link. */
wait_time = 256;
do {
- clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
- qla2x00_configure_loop(ha);
+ clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ qla2x00_configure_loop(vha);
wait_time--;
- } while (!atomic_read(&ha->loop_down_timer) &&
- !(test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) &&
- wait_time &&
- (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)));
+ } while (!atomic_read(&vha->loop_down_timer) &&
+ !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
+ && wait_time && (test_bit(LOOP_RESYNC_NEEDED,
+ &vha->dpc_flags)));
}
/* if no cable then assume it's good */
- if ((ha->device_flags & DFLG_NO_CABLE))
+ if ((vha->device_flags & DFLG_NO_CABLE))
status = 0;
DEBUG(printk("%s(): Configure loop done, status = 0x%x\n",
@@ -3418,6 +3466,46 @@ qla2x00_restart_isp(scsi_qla_host_t *ha)
return (status);
}
+static int
+qla25xx_init_queues(struct qla_hw_data *ha)
+{
+ struct rsp_que *rsp = NULL;
+ struct req_que *req = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ int ret = -1;
+ int i;
+
+ for (i = 1; i < ha->max_queues; i++) {
+ rsp = ha->rsp_q_map[i];
+ if (rsp) {
+ rsp->options &= ~BIT_0;
+ ret = qla25xx_init_rsp_que(base_vha, rsp, rsp->options);
+ if (ret != QLA_SUCCESS)
+ DEBUG2_17(printk(KERN_WARNING
+ "%s Rsp que:%d init failed\n", __func__,
+ rsp->id));
+ else
+ DEBUG2_17(printk(KERN_INFO
+ "%s Rsp que:%d inited\n", __func__,
+ rsp->id));
+ }
+ req = ha->req_q_map[i];
+ if (req) {
+ req->options &= ~BIT_0;
+ ret = qla25xx_init_req_que(base_vha, req, req->options);
+ if (ret != QLA_SUCCESS)
+ DEBUG2_17(printk(KERN_WARNING
+ "%s Req que:%d init failed\n", __func__,
+ req->id));
+ else
+ DEBUG2_17(printk(KERN_WARNING
+ "%s Rsp que:%d inited\n", __func__,
+ req->id));
+ }
+ }
+ return ret;
+}
+
/*
* qla2x00_reset_adapter
* Reset adapter.
@@ -3426,12 +3514,13 @@ qla2x00_restart_isp(scsi_qla_host_t *ha)
* ha = adapter block pointer.
*/
void
-qla2x00_reset_adapter(scsi_qla_host_t *ha)
+qla2x00_reset_adapter(scsi_qla_host_t *vha)
{
unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
- ha->flags.online = 0;
+ vha->flags.online = 0;
ha->isp_ops->disable_intrs(ha);
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -3443,12 +3532,13 @@ qla2x00_reset_adapter(scsi_qla_host_t *ha)
}
void
-qla24xx_reset_adapter(scsi_qla_host_t *ha)
+qla24xx_reset_adapter(scsi_qla_host_t *vha)
{
unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
- ha->flags.online = 0;
+ vha->flags.online = 0;
ha->isp_ops->disable_intrs(ha);
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -3462,9 +3552,11 @@ qla24xx_reset_adapter(scsi_qla_host_t *ha)
/* On sparc systems, obtain port and node WWN from firmware
* properties.
*/
-static void qla24xx_nvram_wwn_from_ofw(scsi_qla_host_t *ha, struct nvram_24xx *nv)
+static void qla24xx_nvram_wwn_from_ofw(scsi_qla_host_t *vha,
+ struct nvram_24xx *nv)
{
#ifdef CONFIG_SPARC
+ struct qla_hw_data *ha = vha->hw;
struct pci_dev *pdev = ha->pdev;
struct device_node *dp = pci_device_to_OF_node(pdev);
const u8 *val;
@@ -3481,7 +3573,7 @@ static void qla24xx_nvram_wwn_from_ofw(scsi_qla_host_t *ha, struct nvram_24xx *n
}
int
-qla24xx_nvram_config(scsi_qla_host_t *ha)
+qla24xx_nvram_config(scsi_qla_host_t *vha)
{
int rval;
struct init_cb_24xx *icb;
@@ -3490,6 +3582,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
uint8_t *dptr1, *dptr2;
uint32_t chksum;
uint16_t cnt;
+ struct qla_hw_data *ha = vha->hw;
rval = QLA_SUCCESS;
icb = (struct init_cb_24xx *)ha->init_cb;
@@ -3507,12 +3600,12 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
/* Get VPD data into cache */
ha->vpd = ha->nvram + VPD_OFFSET;
- ha->isp_ops->read_nvram(ha, (uint8_t *)ha->vpd,
+ ha->isp_ops->read_nvram(vha, (uint8_t *)ha->vpd,
ha->nvram_base - FA_NVRAM_FUNC0_ADDR, FA_NVRAM_VPD_SIZE * 4);
/* Get NVRAM data into cache and calculate checksum. */
dptr = (uint32_t *)nv;
- ha->isp_ops->read_nvram(ha, (uint8_t *)dptr, ha->nvram_base,
+ ha->isp_ops->read_nvram(vha, (uint8_t *)dptr, ha->nvram_base,
ha->nvram_size);
for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++)
chksum += le32_to_cpu(*dptr++);
@@ -3557,7 +3650,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
nv->node_name[5] = 0x1c;
nv->node_name[6] = 0x55;
nv->node_name[7] = 0x86;
- qla24xx_nvram_wwn_from_ofw(ha, nv);
+ qla24xx_nvram_wwn_from_ofw(vha, nv);
nv->login_retry_count = __constant_cpu_to_le16(8);
nv->interrupt_delay_timer = __constant_cpu_to_le16(0);
nv->login_timeout = __constant_cpu_to_le16(0);
@@ -3577,7 +3670,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
}
/* Reset Initialization control block */
- memset(icb, 0, sizeof(struct init_cb_24xx));
+ memset(icb, 0, ha->init_cb_size);
/* Copy 1st segment. */
dptr1 = (uint8_t *)icb;
@@ -3600,7 +3693,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
/*
* Setup driver NVRAM options.
*/
- qla2x00_set_model_info(ha, nv->model_name, sizeof(nv->model_name),
+ qla2x00_set_model_info(vha, nv->model_name, sizeof(nv->model_name),
"QLA2462");
/* Use alternate WWN? */
@@ -3639,8 +3732,8 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
ha->serial0 = icb->port_name[5];
ha->serial1 = icb->port_name[6];
ha->serial2 = icb->port_name[7];
- ha->node_name = icb->node_name;
- ha->port_name = icb->port_name;
+ memcpy(vha->node_name, icb->node_name, WWN_SIZE);
+ memcpy(vha->port_name, icb->port_name, WWN_SIZE);
icb->execution_throttle = __constant_cpu_to_le16(0xFFFF);
@@ -3695,7 +3788,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
ha->login_retry_count = ql2xloginretrycount;
/* Enable ZIO. */
- if (!ha->flags.init_done) {
+ if (!vha->flags.init_done) {
ha->zio_mode = le32_to_cpu(icb->firmware_options_2) &
(BIT_3 | BIT_2 | BIT_1 | BIT_0);
ha->zio_timer = le16_to_cpu(icb->interrupt_delay_timer) ?
@@ -3703,12 +3796,12 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
}
icb->firmware_options_2 &= __constant_cpu_to_le32(
~(BIT_3 | BIT_2 | BIT_1 | BIT_0));
- ha->flags.process_response_queue = 0;
+ vha->flags.process_response_queue = 0;
if (ha->zio_mode != QLA_ZIO_DISABLED) {
ha->zio_mode = QLA_ZIO_MODE_6;
DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer delay "
- "(%d us).\n", ha->host_no, ha->zio_mode,
+ "(%d us).\n", vha->host_no, ha->zio_mode,
ha->zio_timer * 100));
qla_printk(KERN_INFO, ha,
"ZIO mode %d enabled; timer delay (%d us).\n",
@@ -3717,36 +3810,37 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
icb->firmware_options_2 |= cpu_to_le32(
(uint32_t)ha->zio_mode);
icb->interrupt_delay_timer = cpu_to_le16(ha->zio_timer);
- ha->flags.process_response_queue = 1;
+ vha->flags.process_response_queue = 1;
}
if (rval) {
DEBUG2_3(printk(KERN_WARNING
- "scsi(%ld): NVRAM configuration failed!\n", ha->host_no));
+ "scsi(%ld): NVRAM configuration failed!\n", vha->host_no));
}
return (rval);
}
static int
-qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr)
+qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr)
{
- int rval;
+ int rval = QLA_SUCCESS;
int segments, fragment;
uint32_t faddr;
uint32_t *dcode, dlen;
uint32_t risc_addr;
uint32_t risc_size;
uint32_t i;
-
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
rval = QLA_SUCCESS;
segments = FA_RISC_CODE_SEGMENTS;
faddr = ha->flt_region_fw;
- dcode = (uint32_t *)ha->request_ring;
+ dcode = (uint32_t *)req->ring;
*srisc_addr = 0;
/* Validate firmware image by checking version. */
- qla24xx_read_flash_data(ha, dcode, faddr + 4, 4);
+ qla24xx_read_flash_data(vha, dcode, faddr + 4, 4);
for (i = 0; i < 4; i++)
dcode[i] = be32_to_cpu(dcode[i]);
if ((dcode[0] == 0xffffffff && dcode[1] == 0xffffffff &&
@@ -3764,7 +3858,7 @@ qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr)
while (segments && rval == QLA_SUCCESS) {
/* Read segment's load information. */
- qla24xx_read_flash_data(ha, dcode, faddr, 4);
+ qla24xx_read_flash_data(vha, dcode, faddr, 4);
risc_addr = be32_to_cpu(dcode[2]);
*srisc_addr = *srisc_addr == 0 ? risc_addr : *srisc_addr;
@@ -3778,17 +3872,17 @@ qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr)
DEBUG7(printk("scsi(%ld): Loading risc segment@ risc "
"addr %x, number of dwords 0x%x, offset 0x%x.\n",
- ha->host_no, risc_addr, dlen, faddr));
+ vha->host_no, risc_addr, dlen, faddr));
- qla24xx_read_flash_data(ha, dcode, faddr, dlen);
+ qla24xx_read_flash_data(vha, dcode, faddr, dlen);
for (i = 0; i < dlen; i++)
dcode[i] = swab32(dcode[i]);
- rval = qla2x00_load_ram(ha, ha->request_dma, risc_addr,
+ rval = qla2x00_load_ram(vha, req->dma, risc_addr,
dlen);
if (rval) {
DEBUG(printk("scsi(%ld):[ERROR] Failed to load "
- "segment %d of firmware\n", ha->host_no,
+ "segment %d of firmware\n", vha->host_no,
fragment));
qla_printk(KERN_WARNING, ha,
"[ERROR] Failed to load segment %d of "
@@ -3812,16 +3906,18 @@ qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr)
#define QLA_FW_URL "ftp://ftp.qlogic.com/outgoing/linux/firmware/"
int
-qla2x00_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
+qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
{
int rval;
int i, fragment;
uint16_t *wcode, *fwcode;
uint32_t risc_addr, risc_size, fwclen, wlen, *seg;
struct fw_blob *blob;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
/* Load firmware blob. */
- blob = qla2x00_request_firmware(ha);
+ blob = qla2x00_request_firmware(vha);
if (!blob) {
qla_printk(KERN_ERR, ha, "Firmware image unavailable.\n");
qla_printk(KERN_ERR, ha, "Firmware images can be retrieved "
@@ -3831,7 +3927,7 @@ qla2x00_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
rval = QLA_SUCCESS;
- wcode = (uint16_t *)ha->request_ring;
+ wcode = (uint16_t *)req->ring;
*srisc_addr = 0;
fwcode = (uint16_t *)blob->fw->data;
fwclen = 0;
@@ -3878,17 +3974,17 @@ qla2x00_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
wlen = risc_size;
DEBUG7(printk("scsi(%ld): Loading risc segment@ risc "
- "addr %x, number of words 0x%x.\n", ha->host_no,
+ "addr %x, number of words 0x%x.\n", vha->host_no,
risc_addr, wlen));
for (i = 0; i < wlen; i++)
wcode[i] = swab16(fwcode[i]);
- rval = qla2x00_load_ram(ha, ha->request_dma, risc_addr,
+ rval = qla2x00_load_ram(vha, req->dma, risc_addr,
wlen);
if (rval) {
DEBUG(printk("scsi(%ld):[ERROR] Failed to load "
- "segment %d of firmware\n", ha->host_no,
+ "segment %d of firmware\n", vha->host_no,
fragment));
qla_printk(KERN_WARNING, ha,
"[ERROR] Failed to load segment %d of "
@@ -3912,7 +4008,7 @@ fail_fw_integrity:
}
int
-qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
+qla24xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
{
int rval;
int segments, fragment;
@@ -3922,9 +4018,11 @@ qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
uint32_t i;
struct fw_blob *blob;
uint32_t *fwcode, fwclen;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
/* Load firmware blob. */
- blob = qla2x00_request_firmware(ha);
+ blob = qla2x00_request_firmware(vha);
if (!blob) {
qla_printk(KERN_ERR, ha, "Firmware image unavailable.\n");
qla_printk(KERN_ERR, ha, "Firmware images can be retrieved "
@@ -3933,13 +4031,13 @@ qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
/* Try to load RISC code from flash. */
qla_printk(KERN_ERR, ha, "Attempting to load (potentially "
"outdated) firmware from flash.\n");
- return qla24xx_load_risc_flash(ha, srisc_addr);
+ return qla24xx_load_risc_flash(vha, srisc_addr);
}
rval = QLA_SUCCESS;
segments = FA_RISC_CODE_SEGMENTS;
- dcode = (uint32_t *)ha->request_ring;
+ dcode = (uint32_t *)req->ring;
*srisc_addr = 0;
fwcode = (uint32_t *)blob->fw->data;
fwclen = 0;
@@ -3987,17 +4085,17 @@ qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
dlen = risc_size;
DEBUG7(printk("scsi(%ld): Loading risc segment@ risc "
- "addr %x, number of dwords 0x%x.\n", ha->host_no,
+ "addr %x, number of dwords 0x%x.\n", vha->host_no,
risc_addr, dlen));
for (i = 0; i < dlen; i++)
dcode[i] = swab32(fwcode[i]);
- rval = qla2x00_load_ram(ha, ha->request_dma, risc_addr,
+ rval = qla2x00_load_ram(vha, req->dma, risc_addr,
dlen);
if (rval) {
DEBUG(printk("scsi(%ld):[ERROR] Failed to load "
- "segment %d of firmware\n", ha->host_no,
+ "segment %d of firmware\n", vha->host_no,
fragment));
qla_printk(KERN_WARNING, ha,
"[ERROR] Failed to load segment %d of "
@@ -4021,49 +4119,53 @@ fail_fw_integrity:
}
void
-qla2x00_try_to_stop_firmware(scsi_qla_host_t *ha)
+qla2x00_try_to_stop_firmware(scsi_qla_host_t *vha)
{
int ret, retries;
+ struct qla_hw_data *ha = vha->hw;
if (!IS_FWI2_CAPABLE(ha))
return;
if (!ha->fw_major_version)
return;
- ret = qla2x00_stop_firmware(ha);
+ ret = qla2x00_stop_firmware(vha);
for (retries = 5; ret != QLA_SUCCESS && ret != QLA_FUNCTION_TIMEOUT &&
retries ; retries--) {
- ha->isp_ops->reset_chip(ha);
- if (ha->isp_ops->chip_diag(ha) != QLA_SUCCESS)
+ ha->isp_ops->reset_chip(vha);
+ if (ha->isp_ops->chip_diag(vha) != QLA_SUCCESS)
continue;
- if (qla2x00_setup_chip(ha) != QLA_SUCCESS)
+ if (qla2x00_setup_chip(vha) != QLA_SUCCESS)
continue;
qla_printk(KERN_INFO, ha,
"Attempting retry of stop-firmware command...\n");
- ret = qla2x00_stop_firmware(ha);
+ ret = qla2x00_stop_firmware(vha);
}
}
int
-qla24xx_configure_vhba(scsi_qla_host_t *ha)
+qla24xx_configure_vhba(scsi_qla_host_t *vha)
{
int rval = QLA_SUCCESS;
uint16_t mb[MAILBOX_REGISTER_COUNT];
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
- if (!ha->parent)
+ if (!vha->vp_idx)
return -EINVAL;
- rval = qla2x00_fw_ready(ha->parent);
+ rval = qla2x00_fw_ready(base_vha);
if (rval == QLA_SUCCESS) {
- clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
- qla2x00_marker(ha, 0, 0, MK_SYNC_ALL);
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+ qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
}
- ha->flags.management_server_logged_in = 0;
+ vha->flags.management_server_logged_in = 0;
/* Login to SNS first */
- qla24xx_login_fabric(ha->parent, NPH_SNS, 0xff, 0xff, 0xfc,
- mb, BIT_1);
+ ha->isp_ops->fabric_login(vha, NPH_SNS, 0xff, 0xff, 0xfc, mb, BIT_1);
if (mb[0] != MBS_COMMAND_COMPLETE) {
DEBUG15(qla_printk(KERN_INFO, ha,
"Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x "
@@ -4072,11 +4174,11 @@ qla24xx_configure_vhba(scsi_qla_host_t *ha)
return (QLA_FUNCTION_FAILED);
}
- atomic_set(&ha->loop_down_timer, 0);
- atomic_set(&ha->loop_state, LOOP_UP);
- set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
- set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
- rval = qla2x00_loop_resync(ha->parent);
+ atomic_set(&vha->loop_down_timer, 0);
+ atomic_set(&vha->loop_state, LOOP_UP);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ rval = qla2x00_loop_resync(base_vha);
return rval;
}
@@ -4087,9 +4189,10 @@ static LIST_HEAD(qla_cs84xx_list);
static DEFINE_MUTEX(qla_cs84xx_mutex);
static struct qla_chip_state_84xx *
-qla84xx_get_chip(struct scsi_qla_host *ha)
+qla84xx_get_chip(struct scsi_qla_host *vha)
{
struct qla_chip_state_84xx *cs84xx;
+ struct qla_hw_data *ha = vha->hw;
mutex_lock(&qla_cs84xx_mutex);
@@ -4129,21 +4232,23 @@ __qla84xx_chip_release(struct kref *kref)
}
void
-qla84xx_put_chip(struct scsi_qla_host *ha)
+qla84xx_put_chip(struct scsi_qla_host *vha)
{
+ struct qla_hw_data *ha = vha->hw;
if (ha->cs84xx)
kref_put(&ha->cs84xx->kref, __qla84xx_chip_release);
}
static int
-qla84xx_init_chip(scsi_qla_host_t *ha)
+qla84xx_init_chip(scsi_qla_host_t *vha)
{
int rval;
uint16_t status[2];
+ struct qla_hw_data *ha = vha->hw;
mutex_lock(&ha->cs84xx->fw_update_mutex);
- rval = qla84xx_verify_chip(ha, status);
+ rval = qla84xx_verify_chip(vha, status);
mutex_unlock(&ha->cs84xx->fw_update_mutex);
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index e90afad120ee..5e0a7095c9f2 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -32,47 +32,15 @@ qla2x00_debounce_register(volatile uint16_t __iomem *addr)
}
static inline void
-qla2x00_poll(scsi_qla_host_t *ha)
+qla2x00_poll(struct rsp_que *rsp)
{
unsigned long flags;
-
+ struct qla_hw_data *ha = rsp->hw;
local_irq_save(flags);
- ha->isp_ops->intr_handler(0, ha);
+ ha->isp_ops->intr_handler(0, rsp);
local_irq_restore(flags);
}
-static __inline__ scsi_qla_host_t *
-to_qla_parent(scsi_qla_host_t *ha)
-{
- return ha->parent ? ha->parent : ha;
-}
-
-/**
- * qla2x00_issue_marker() - Issue a Marker IOCB if necessary.
- * @ha: HA context
- * @ha_locked: is function called with the hardware lock
- *
- * Returns non-zero if a failure occurred, else zero.
- */
-static inline int
-qla2x00_issue_marker(scsi_qla_host_t *ha, int ha_locked)
-{
- /* Send marker if required */
- if (ha->marker_needed != 0) {
- if (ha_locked) {
- if (__qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) !=
- QLA_SUCCESS)
- return (QLA_FUNCTION_FAILED);
- } else {
- if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) !=
- QLA_SUCCESS)
- return (QLA_FUNCTION_FAILED);
- }
- ha->marker_needed = 0;
- }
- return (QLA_SUCCESS);
-}
-
static inline uint8_t *
host_to_fcp_swap(uint8_t *fcp, uint32_t bsize)
{
@@ -87,11 +55,12 @@ host_to_fcp_swap(uint8_t *fcp, uint32_t bsize)
}
static inline int
-qla2x00_is_reserved_id(scsi_qla_host_t *ha, uint16_t loop_id)
+qla2x00_is_reserved_id(scsi_qla_host_t *vha, uint16_t loop_id)
{
+ struct qla_hw_data *ha = vha->hw;
if (IS_FWI2_CAPABLE(ha))
return (loop_id > NPH_LAST_HANDLE);
- return ((loop_id > ha->last_loop_id && loop_id < SNS_FIRST_LOOP_ID) ||
+ return ((loop_id > ha->max_loop_id && loop_id < SNS_FIRST_LOOP_ID) ||
loop_id == MANAGEMENT_SERVER || loop_id == BROADCAST);
-};
+}
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 85bc0a48598b..5bedc9d05942 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -11,8 +11,9 @@
#include <scsi/scsi_tcq.h>
-static request_t *qla2x00_req_pkt(scsi_qla_host_t *ha);
-static void qla2x00_isp_cmd(scsi_qla_host_t *ha);
+static request_t *qla2x00_req_pkt(struct scsi_qla_host *, struct req_que *,
+ struct rsp_que *rsp);
+static void qla2x00_isp_cmd(struct scsi_qla_host *, struct req_que *);
/**
* qla2x00_get_cmd_direction() - Determine control_flag data direction.
@@ -30,11 +31,11 @@ qla2x00_get_cmd_direction(srb_t *sp)
/* Set transfer direction */
if (sp->cmd->sc_data_direction == DMA_TO_DEVICE) {
cflags = CF_WRITE;
- sp->fcport->ha->qla_stats.output_bytes +=
+ sp->fcport->vha->hw->qla_stats.output_bytes +=
scsi_bufflen(sp->cmd);
} else if (sp->cmd->sc_data_direction == DMA_FROM_DEVICE) {
cflags = CF_READ;
- sp->fcport->ha->qla_stats.input_bytes +=
+ sp->fcport->vha->hw->qla_stats.input_bytes +=
scsi_bufflen(sp->cmd);
}
return (cflags);
@@ -91,20 +92,19 @@ qla2x00_calc_iocbs_64(uint16_t dsds)
* Returns a pointer to the Continuation Type 0 IOCB packet.
*/
static inline cont_entry_t *
-qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *ha)
+qla2x00_prep_cont_type0_iocb(struct req_que *req, struct scsi_qla_host *vha)
{
cont_entry_t *cont_pkt;
-
/* Adjust ring index. */
- ha->req_ring_index++;
- if (ha->req_ring_index == ha->request_q_length) {
- ha->req_ring_index = 0;
- ha->request_ring_ptr = ha->request_ring;
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
} else {
- ha->request_ring_ptr++;
+ req->ring_ptr++;
}
- cont_pkt = (cont_entry_t *)ha->request_ring_ptr;
+ cont_pkt = (cont_entry_t *)req->ring_ptr;
/* Load packet defaults. */
*((uint32_t *)(&cont_pkt->entry_type)) =
@@ -120,20 +120,20 @@ qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *ha)
* Returns a pointer to the continuation type 1 IOCB packet.
*/
static inline cont_a64_entry_t *
-qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *ha)
+qla2x00_prep_cont_type1_iocb(struct req_que *req, scsi_qla_host_t *vha)
{
cont_a64_entry_t *cont_pkt;
/* Adjust ring index. */
- ha->req_ring_index++;
- if (ha->req_ring_index == ha->request_q_length) {
- ha->req_ring_index = 0;
- ha->request_ring_ptr = ha->request_ring;
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
} else {
- ha->request_ring_ptr++;
+ req->ring_ptr++;
}
- cont_pkt = (cont_a64_entry_t *)ha->request_ring_ptr;
+ cont_pkt = (cont_a64_entry_t *)req->ring_ptr;
/* Load packet defaults. */
*((uint32_t *)(&cont_pkt->entry_type)) =
@@ -155,10 +155,11 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
{
uint16_t avail_dsds;
uint32_t *cur_dsd;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
struct scsi_cmnd *cmd;
struct scatterlist *sg;
int i;
+ struct req_que *req;
cmd = sp->cmd;
@@ -172,7 +173,8 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
return;
}
- ha = sp->ha;
+ vha = sp->vha;
+ req = sp->que;
cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp));
@@ -190,7 +192,7 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
* Seven DSDs are available in the Continuation
* Type 0 IOCB.
*/
- cont_pkt = qla2x00_prep_cont_type0_iocb(ha);
+ cont_pkt = qla2x00_prep_cont_type0_iocb(req, vha);
cur_dsd = (uint32_t *)&cont_pkt->dseg_0_address;
avail_dsds = 7;
}
@@ -214,10 +216,11 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
{
uint16_t avail_dsds;
uint32_t *cur_dsd;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
struct scsi_cmnd *cmd;
struct scatterlist *sg;
int i;
+ struct req_que *req;
cmd = sp->cmd;
@@ -231,7 +234,8 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
return;
}
- ha = sp->ha;
+ vha = sp->vha;
+ req = sp->que;
cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp));
@@ -250,7 +254,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
* Five DSDs are available in the Continuation
* Type 1 IOCB.
*/
- cont_pkt = qla2x00_prep_cont_type1_iocb(ha);
+ cont_pkt = qla2x00_prep_cont_type1_iocb(req, vha);
cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
avail_dsds = 5;
}
@@ -274,7 +278,7 @@ qla2x00_start_scsi(srb_t *sp)
{
int ret, nseg;
unsigned long flags;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
struct scsi_cmnd *cmd;
uint32_t *clr_ptr;
uint32_t index;
@@ -284,33 +288,39 @@ qla2x00_start_scsi(srb_t *sp)
uint16_t req_cnt;
uint16_t tot_dsds;
struct device_reg_2xxx __iomem *reg;
+ struct qla_hw_data *ha;
+ struct req_que *req;
+ struct rsp_que *rsp;
/* Setup device pointers. */
ret = 0;
- ha = sp->ha;
+ vha = sp->vha;
+ ha = vha->hw;
reg = &ha->iobase->isp;
cmd = sp->cmd;
+ req = ha->req_q_map[0];
+ rsp = ha->rsp_q_map[0];
/* So we know we haven't pci_map'ed anything yet */
tot_dsds = 0;
/* Send marker if required */
- if (ha->marker_needed != 0) {
- if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) {
+ if (vha->marker_needed != 0) {
+ if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL)
+ != QLA_SUCCESS)
return (QLA_FUNCTION_FAILED);
- }
- ha->marker_needed = 0;
+ vha->marker_needed = 0;
}
/* Acquire ring specific lock */
spin_lock_irqsave(&ha->hardware_lock, flags);
/* Check for room in outstanding command list. */
- handle = ha->current_outstanding_cmd;
+ handle = req->current_outstanding_cmd;
for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
handle++;
if (handle == MAX_OUTSTANDING_COMMANDS)
handle = 1;
- if (!ha->outstanding_cmds[handle])
+ if (!req->outstanding_cmds[handle])
break;
}
if (index == MAX_OUTSTANDING_COMMANDS)
@@ -329,25 +339,26 @@ qla2x00_start_scsi(srb_t *sp)
/* Calculate the number of request entries needed. */
req_cnt = ha->isp_ops->calc_req_entries(tot_dsds);
- if (ha->req_q_cnt < (req_cnt + 2)) {
+ if (req->cnt < (req_cnt + 2)) {
cnt = RD_REG_WORD_RELAXED(ISP_REQ_Q_OUT(ha, reg));
- if (ha->req_ring_index < cnt)
- ha->req_q_cnt = cnt - ha->req_ring_index;
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
else
- ha->req_q_cnt = ha->request_q_length -
- (ha->req_ring_index - cnt);
+ req->cnt = req->length -
+ (req->ring_index - cnt);
}
- if (ha->req_q_cnt < (req_cnt + 2))
+ if (req->cnt < (req_cnt + 2))
goto queuing_error;
/* Build command packet */
- ha->current_outstanding_cmd = handle;
- ha->outstanding_cmds[handle] = sp;
- sp->ha = ha;
+ req->current_outstanding_cmd = handle;
+ req->outstanding_cmds[handle] = sp;
+ sp->vha = vha;
+ sp->que = req;
sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
- ha->req_q_cnt -= req_cnt;
+ req->cnt -= req_cnt;
- cmd_pkt = (cmd_entry_t *)ha->request_ring_ptr;
+ cmd_pkt = (cmd_entry_t *)req->ring_ptr;
cmd_pkt->handle = handle;
/* Zero out remaining portion of packet. */
clr_ptr = (uint32_t *)cmd_pkt + 2;
@@ -373,23 +384,23 @@ qla2x00_start_scsi(srb_t *sp)
wmb();
/* Adjust ring index. */
- ha->req_ring_index++;
- if (ha->req_ring_index == ha->request_q_length) {
- ha->req_ring_index = 0;
- ha->request_ring_ptr = ha->request_ring;
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
} else
- ha->request_ring_ptr++;
+ req->ring_ptr++;
sp->flags |= SRB_DMA_VALID;
/* Set chip new ring index. */
- WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), ha->req_ring_index);
+ WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), req->ring_index);
RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, reg)); /* PCI Posting. */
/* Manage unprocessed RIO/ZIO commands in response queue. */
- if (ha->flags.process_response_queue &&
- ha->response_ring_ptr->signature != RESPONSE_PROCESSED)
- qla2x00_process_response_queue(ha);
+ if (vha->flags.process_response_queue &&
+ rsp->ring_ptr->signature != RESPONSE_PROCESSED)
+ qla2x00_process_response_queue(rsp);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return (QLA_SUCCESS);
@@ -415,18 +426,20 @@ queuing_error:
* Returns non-zero if a failure occurred, else zero.
*/
int
-__qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
- uint8_t type)
+__qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
+ struct rsp_que *rsp, uint16_t loop_id,
+ uint16_t lun, uint8_t type)
{
mrk_entry_t *mrk;
struct mrk_entry_24xx *mrk24;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
mrk24 = NULL;
- mrk = (mrk_entry_t *)qla2x00_req_pkt(pha);
+ mrk = (mrk_entry_t *)qla2x00_req_pkt(vha, req, rsp);
if (mrk == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Marker IOCB.\n",
- __func__, ha->host_no));
+ __func__, base_vha->host_no));
return (QLA_FUNCTION_FAILED);
}
@@ -440,7 +453,7 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
mrk24->lun[1] = LSB(lun);
mrk24->lun[2] = MSB(lun);
host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun));
- mrk24->vp_index = ha->vp_idx;
+ mrk24->vp_index = vha->vp_idx;
} else {
SET_TARGET_ID(ha, mrk->target, loop_id);
mrk->lun = cpu_to_le16(lun);
@@ -448,22 +461,22 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
}
wmb();
- qla2x00_isp_cmd(pha);
+ qla2x00_isp_cmd(vha, req);
return (QLA_SUCCESS);
}
int
-qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
- uint8_t type)
+qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
+ struct rsp_que *rsp, uint16_t loop_id, uint16_t lun,
+ uint8_t type)
{
int ret;
unsigned long flags = 0;
- scsi_qla_host_t *pha = to_qla_parent(ha);
- spin_lock_irqsave(&pha->hardware_lock, flags);
- ret = __qla2x00_marker(ha, loop_id, lun, type);
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_lock_irqsave(&vha->hw->hardware_lock, flags);
+ ret = __qla2x00_marker(vha, req, rsp, loop_id, lun, type);
+ spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
return (ret);
}
@@ -477,9 +490,11 @@ qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
* Returns NULL if function failed, else, a pointer to the request packet.
*/
static request_t *
-qla2x00_req_pkt(scsi_qla_host_t *ha)
+qla2x00_req_pkt(struct scsi_qla_host *vha, struct req_que *req,
+ struct rsp_que *rsp)
{
- device_reg_t __iomem *reg = ha->iobase;
+ struct qla_hw_data *ha = vha->hw;
+ device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id);
request_t *pkt = NULL;
uint16_t cnt;
uint32_t *dword_ptr;
@@ -488,24 +503,29 @@ qla2x00_req_pkt(scsi_qla_host_t *ha)
/* Wait 1 second for slot. */
for (timer = HZ; timer; timer--) {
- if ((req_cnt + 2) >= ha->req_q_cnt) {
+ if ((req_cnt + 2) >= req->cnt) {
/* Calculate number of free request entries. */
- if (IS_FWI2_CAPABLE(ha))
- cnt = (uint16_t)RD_REG_DWORD(
- &reg->isp24.req_q_out);
- else
- cnt = qla2x00_debounce_register(
- ISP_REQ_Q_OUT(ha, &reg->isp));
- if (ha->req_ring_index < cnt)
- ha->req_q_cnt = cnt - ha->req_ring_index;
+ if (ha->mqenable)
+ cnt = (uint16_t)
+ RD_REG_DWORD(&reg->isp25mq.req_q_out);
+ else {
+ if (IS_FWI2_CAPABLE(ha))
+ cnt = (uint16_t)RD_REG_DWORD(
+ &reg->isp24.req_q_out);
+ else
+ cnt = qla2x00_debounce_register(
+ ISP_REQ_Q_OUT(ha, &reg->isp));
+ }
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
else
- ha->req_q_cnt = ha->request_q_length -
- (ha->req_ring_index - cnt);
+ req->cnt = req->length -
+ (req->ring_index - cnt);
}
/* If room for request in request ring. */
- if ((req_cnt + 2) < ha->req_q_cnt) {
- ha->req_q_cnt--;
- pkt = ha->request_ring_ptr;
+ if ((req_cnt + 2) < req->cnt) {
+ req->cnt--;
+ pkt = req->ring_ptr;
/* Zero out packet. */
dword_ptr = (uint32_t *)pkt;
@@ -513,7 +533,7 @@ qla2x00_req_pkt(scsi_qla_host_t *ha)
*dword_ptr++ = 0;
/* Set system defined field. */
- pkt->sys_define = (uint8_t)ha->req_ring_index;
+ pkt->sys_define = (uint8_t)req->ring_index;
/* Set entry count. */
pkt->entry_count = 1;
@@ -522,15 +542,14 @@ qla2x00_req_pkt(scsi_qla_host_t *ha)
}
/* Release ring specific lock */
- spin_unlock(&ha->hardware_lock);
+ spin_unlock_irq(&ha->hardware_lock);
udelay(2); /* 2 us */
/* Check for pending interrupts. */
/* During init we issue marker directly */
- if (!ha->marker_needed && !ha->flags.init_done)
- qla2x00_poll(ha);
-
+ if (!vha->marker_needed && !vha->flags.init_done)
+ qla2x00_poll(rsp);
spin_lock_irq(&ha->hardware_lock);
}
if (!pkt) {
@@ -547,29 +566,38 @@ qla2x00_req_pkt(scsi_qla_host_t *ha)
* Note: The caller must hold the hardware lock before calling this routine.
*/
static void
-qla2x00_isp_cmd(scsi_qla_host_t *ha)
+qla2x00_isp_cmd(struct scsi_qla_host *vha, struct req_que *req)
{
- device_reg_t __iomem *reg = ha->iobase;
+ struct qla_hw_data *ha = vha->hw;
+ device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id);
+ struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp;
DEBUG5(printk("%s(): IOCB data:\n", __func__));
DEBUG5(qla2x00_dump_buffer(
- (uint8_t *)ha->request_ring_ptr, REQUEST_ENTRY_SIZE));
+ (uint8_t *)req->ring_ptr, REQUEST_ENTRY_SIZE));
/* Adjust ring index. */
- ha->req_ring_index++;
- if (ha->req_ring_index == ha->request_q_length) {
- ha->req_ring_index = 0;
- ha->request_ring_ptr = ha->request_ring;
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
} else
- ha->request_ring_ptr++;
+ req->ring_ptr++;
/* Set chip new ring index. */
- if (IS_FWI2_CAPABLE(ha)) {
- WRT_REG_DWORD(&reg->isp24.req_q_in, ha->req_ring_index);
- RD_REG_DWORD_RELAXED(&reg->isp24.req_q_in);
- } else {
- WRT_REG_WORD(ISP_REQ_Q_IN(ha, &reg->isp), ha->req_ring_index);
- RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, &reg->isp));
+ if (ha->mqenable) {
+ WRT_REG_DWORD(&reg->isp25mq.req_q_in, req->ring_index);
+ RD_REG_DWORD(&ioreg->hccr);
+ }
+ else {
+ if (IS_FWI2_CAPABLE(ha)) {
+ WRT_REG_DWORD(&reg->isp24.req_q_in, req->ring_index);
+ RD_REG_DWORD_RELAXED(&reg->isp24.req_q_in);
+ } else {
+ WRT_REG_WORD(ISP_REQ_Q_IN(ha, &reg->isp),
+ req->ring_index);
+ RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, &reg->isp));
+ }
}
}
@@ -610,10 +638,11 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
{
uint16_t avail_dsds;
uint32_t *cur_dsd;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
struct scsi_cmnd *cmd;
struct scatterlist *sg;
int i;
+ struct req_que *req;
cmd = sp->cmd;
@@ -627,18 +656,19 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
return;
}
- ha = sp->ha;
+ vha = sp->vha;
+ req = sp->que;
/* Set transfer direction */
if (cmd->sc_data_direction == DMA_TO_DEVICE) {
cmd_pkt->task_mgmt_flags =
__constant_cpu_to_le16(TMF_WRITE_DATA);
- sp->fcport->ha->qla_stats.output_bytes +=
+ sp->fcport->vha->hw->qla_stats.output_bytes +=
scsi_bufflen(sp->cmd);
} else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
cmd_pkt->task_mgmt_flags =
__constant_cpu_to_le16(TMF_READ_DATA);
- sp->fcport->ha->qla_stats.input_bytes +=
+ sp->fcport->vha->hw->qla_stats.input_bytes +=
scsi_bufflen(sp->cmd);
}
@@ -658,7 +688,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
* Five DSDs are available in the Continuation
* Type 1 IOCB.
*/
- cont_pkt = qla2x00_prep_cont_type1_iocb(ha);
+ cont_pkt = qla2x00_prep_cont_type1_iocb(req, vha);
cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
avail_dsds = 5;
}
@@ -683,8 +713,6 @@ qla24xx_start_scsi(srb_t *sp)
{
int ret, nseg;
unsigned long flags;
- scsi_qla_host_t *ha, *pha;
- struct scsi_cmnd *cmd;
uint32_t *clr_ptr;
uint32_t index;
uint32_t handle;
@@ -692,35 +720,45 @@ qla24xx_start_scsi(srb_t *sp)
uint16_t cnt;
uint16_t req_cnt;
uint16_t tot_dsds;
- struct device_reg_24xx __iomem *reg;
+ struct req_que *req = NULL;
+ struct rsp_que *rsp = NULL;
+ struct scsi_cmnd *cmd = sp->cmd;
+ struct scsi_qla_host *vha = sp->vha;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t que_id;
/* Setup device pointers. */
ret = 0;
- ha = sp->ha;
- pha = to_qla_parent(ha);
- reg = &ha->iobase->isp24;
- cmd = sp->cmd;
+ que_id = vha->req_ques[0];
+
+ req = ha->req_q_map[que_id];
+ sp->que = req;
+
+ if (req->rsp)
+ rsp = req->rsp;
+ else
+ rsp = ha->rsp_q_map[que_id];
/* So we know we haven't pci_map'ed anything yet */
tot_dsds = 0;
/* Send marker if required */
- if (ha->marker_needed != 0) {
- if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) {
+ if (vha->marker_needed != 0) {
+ if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL)
+ != QLA_SUCCESS)
return QLA_FUNCTION_FAILED;
- }
- ha->marker_needed = 0;
+ vha->marker_needed = 0;
}
/* Acquire ring specific lock */
- spin_lock_irqsave(&pha->hardware_lock, flags);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
/* Check for room in outstanding command list. */
- handle = ha->current_outstanding_cmd;
+ handle = req->current_outstanding_cmd;
for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
handle++;
if (handle == MAX_OUTSTANDING_COMMANDS)
handle = 1;
- if (!ha->outstanding_cmds[handle])
+ if (!req->outstanding_cmds[handle])
break;
}
if (index == MAX_OUTSTANDING_COMMANDS)
@@ -738,25 +776,26 @@ qla24xx_start_scsi(srb_t *sp)
tot_dsds = nseg;
req_cnt = qla24xx_calc_iocbs(tot_dsds);
- if (ha->req_q_cnt < (req_cnt + 2)) {
- cnt = (uint16_t)RD_REG_DWORD_RELAXED(&reg->req_q_out);
- if (ha->req_ring_index < cnt)
- ha->req_q_cnt = cnt - ha->req_ring_index;
+ if (req->cnt < (req_cnt + 2)) {
+ cnt = ha->isp_ops->rd_req_reg(ha, req->id);
+
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
else
- ha->req_q_cnt = ha->request_q_length -
- (ha->req_ring_index - cnt);
+ req->cnt = req->length -
+ (req->ring_index - cnt);
}
- if (ha->req_q_cnt < (req_cnt + 2))
+ if (req->cnt < (req_cnt + 2))
goto queuing_error;
/* Build command packet. */
- ha->current_outstanding_cmd = handle;
- ha->outstanding_cmds[handle] = sp;
- sp->ha = ha;
+ req->current_outstanding_cmd = handle;
+ req->outstanding_cmds[handle] = sp;
+ sp->vha = vha;
sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
- ha->req_q_cnt -= req_cnt;
+ req->cnt -= req_cnt;
- cmd_pkt = (struct cmd_type_7 *)ha->request_ring_ptr;
+ cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
cmd_pkt->handle = handle;
/* Zero out remaining portion of packet. */
@@ -789,32 +828,63 @@ qla24xx_start_scsi(srb_t *sp)
wmb();
/* Adjust ring index. */
- ha->req_ring_index++;
- if (ha->req_ring_index == ha->request_q_length) {
- ha->req_ring_index = 0;
- ha->request_ring_ptr = ha->request_ring;
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
} else
- ha->request_ring_ptr++;
+ req->ring_ptr++;
sp->flags |= SRB_DMA_VALID;
/* Set chip new ring index. */
- WRT_REG_DWORD(&reg->req_q_in, ha->req_ring_index);
- RD_REG_DWORD_RELAXED(&reg->req_q_in); /* PCI Posting. */
+ ha->isp_ops->wrt_req_reg(ha, req->id, req->ring_index);
/* Manage unprocessed RIO/ZIO commands in response queue. */
- if (ha->flags.process_response_queue &&
- ha->response_ring_ptr->signature != RESPONSE_PROCESSED)
- qla24xx_process_response_queue(ha);
+ if (vha->flags.process_response_queue &&
+ rsp->ring_ptr->signature != RESPONSE_PROCESSED)
+ qla24xx_process_response_queue(rsp);
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
return QLA_SUCCESS;
queuing_error:
if (tot_dsds)
scsi_dma_unmap(cmd);
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
return QLA_FUNCTION_FAILED;
}
+
+uint16_t
+qla24xx_rd_req_reg(struct qla_hw_data *ha, uint16_t id)
+{
+ device_reg_t __iomem *reg = (void *) ha->iobase;
+ return RD_REG_DWORD_RELAXED(&reg->isp24.req_q_out);
+}
+
+uint16_t
+qla25xx_rd_req_reg(struct qla_hw_data *ha, uint16_t id)
+{
+ device_reg_t __iomem *reg = (void *) ha->mqiobase + QLA_QUE_PAGE * id;
+ return RD_REG_DWORD_RELAXED(&reg->isp25mq.req_q_out);
+}
+
+void
+qla24xx_wrt_req_reg(struct qla_hw_data *ha, uint16_t id, uint16_t index)
+{
+ device_reg_t __iomem *reg = (void *) ha->iobase;
+ WRT_REG_DWORD(&reg->isp24.req_q_in, index);
+ RD_REG_DWORD_RELAXED(&reg->isp24.req_q_in);
+}
+
+void
+qla25xx_wrt_req_reg(struct qla_hw_data *ha, uint16_t id, uint16_t index)
+{
+ device_reg_t __iomem *reg = (void *) ha->mqiobase + QLA_QUE_PAGE * id;
+ struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp;
+ WRT_REG_DWORD(&reg->isp25mq.req_q_in, index);
+ RD_REG_DWORD(&ioreg->hccr); /* PCI posting */
+}
+
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index a76efd99d007..d5fb79a88001 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -10,10 +10,13 @@
#include <scsi/scsi_tcq.h>
static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
-static void qla2x00_process_completed_request(struct scsi_qla_host *, uint32_t);
-static void qla2x00_status_entry(scsi_qla_host_t *, void *);
+static void qla2x00_process_completed_request(struct scsi_qla_host *,
+ struct req_que *, uint32_t);
+static void qla2x00_status_entry(scsi_qla_host_t *, struct rsp_que *, void *);
static void qla2x00_status_cont_entry(scsi_qla_host_t *, sts_cont_entry_t *);
-static void qla2x00_error_entry(scsi_qla_host_t *, sts_entry_t *);
+static void qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *,
+ sts_entry_t *);
+static struct scsi_qla_host *qla2x00_get_rsp_host(struct rsp_que *);
/**
* qla2100_intr_handler() - Process interrupts for the ISP2100 and ISP2200.
@@ -27,24 +30,28 @@ static void qla2x00_error_entry(scsi_qla_host_t *, sts_entry_t *);
irqreturn_t
qla2100_intr_handler(int irq, void *dev_id)
{
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
struct device_reg_2xxx __iomem *reg;
int status;
unsigned long iter;
uint16_t hccr;
uint16_t mb[4];
+ struct rsp_que *rsp;
- ha = (scsi_qla_host_t *) dev_id;
- if (!ha) {
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
printk(KERN_INFO
- "%s(): NULL host pointer\n", __func__);
+ "%s(): NULL response queue pointer\n", __func__);
return (IRQ_NONE);
}
+ ha = rsp->hw;
reg = &ha->iobase->isp;
status = 0;
spin_lock(&ha->hardware_lock);
+ vha = qla2x00_get_rsp_host(rsp);
for (iter = 50; iter--; ) {
hccr = RD_REG_WORD(&reg->hccr);
if (hccr & HCCR_RISC_PAUSE) {
@@ -59,8 +66,8 @@ qla2100_intr_handler(int irq, void *dev_id)
WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
RD_REG_WORD(&reg->hccr);
- ha->isp_ops->fw_dump(ha, 1);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ ha->isp_ops->fw_dump(vha, 1);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
} else if ((RD_REG_WORD(&reg->istatus) & ISR_RISC_INT) == 0)
break;
@@ -72,24 +79,24 @@ qla2100_intr_handler(int irq, void *dev_id)
/* Get mailbox data. */
mb[0] = RD_MAILBOX_REG(ha, reg, 0);
if (mb[0] > 0x3fff && mb[0] < 0x8000) {
- qla2x00_mbx_completion(ha, mb[0]);
+ qla2x00_mbx_completion(vha, mb[0]);
status |= MBX_INTERRUPT;
} else if (mb[0] > 0x7fff && mb[0] < 0xc000) {
mb[1] = RD_MAILBOX_REG(ha, reg, 1);
mb[2] = RD_MAILBOX_REG(ha, reg, 2);
mb[3] = RD_MAILBOX_REG(ha, reg, 3);
- qla2x00_async_event(ha, mb);
+ qla2x00_async_event(vha, rsp, mb);
} else {
/*EMPTY*/
DEBUG2(printk("scsi(%ld): Unrecognized "
"interrupt type (%d).\n",
- ha->host_no, mb[0]));
+ vha->host_no, mb[0]));
}
/* Release mailbox registers. */
WRT_REG_WORD(&reg->semaphore, 0);
RD_REG_WORD(&reg->semaphore);
} else {
- qla2x00_process_response_queue(ha);
+ qla2x00_process_response_queue(rsp);
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
RD_REG_WORD(&reg->hccr);
@@ -118,25 +125,29 @@ qla2100_intr_handler(int irq, void *dev_id)
irqreturn_t
qla2300_intr_handler(int irq, void *dev_id)
{
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
struct device_reg_2xxx __iomem *reg;
int status;
unsigned long iter;
uint32_t stat;
uint16_t hccr;
uint16_t mb[4];
+ struct rsp_que *rsp;
+ struct qla_hw_data *ha;
- ha = (scsi_qla_host_t *) dev_id;
- if (!ha) {
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
printk(KERN_INFO
- "%s(): NULL host pointer\n", __func__);
+ "%s(): NULL response queue pointer\n", __func__);
return (IRQ_NONE);
}
+ ha = rsp->hw;
reg = &ha->iobase->isp;
status = 0;
spin_lock(&ha->hardware_lock);
+ vha = qla2x00_get_rsp_host(rsp);
for (iter = 50; iter--; ) {
stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
if (stat & HSR_RISC_PAUSED) {
@@ -159,8 +170,8 @@ qla2300_intr_handler(int irq, void *dev_id)
WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
RD_REG_WORD(&reg->hccr);
- ha->isp_ops->fw_dump(ha, 1);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ ha->isp_ops->fw_dump(vha, 1);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
} else if ((stat & HSR_RISC_INT) == 0)
break;
@@ -170,7 +181,7 @@ qla2300_intr_handler(int irq, void *dev_id)
case 0x2:
case 0x10:
case 0x11:
- qla2x00_mbx_completion(ha, MSW(stat));
+ qla2x00_mbx_completion(vha, MSW(stat));
status |= MBX_INTERRUPT;
/* Release mailbox registers. */
@@ -181,26 +192,26 @@ qla2300_intr_handler(int irq, void *dev_id)
mb[1] = RD_MAILBOX_REG(ha, reg, 1);
mb[2] = RD_MAILBOX_REG(ha, reg, 2);
mb[3] = RD_MAILBOX_REG(ha, reg, 3);
- qla2x00_async_event(ha, mb);
+ qla2x00_async_event(vha, rsp, mb);
break;
case 0x13:
- qla2x00_process_response_queue(ha);
+ qla2x00_process_response_queue(rsp);
break;
case 0x15:
mb[0] = MBA_CMPLT_1_16BIT;
mb[1] = MSW(stat);
- qla2x00_async_event(ha, mb);
+ qla2x00_async_event(vha, rsp, mb);
break;
case 0x16:
mb[0] = MBA_SCSI_COMPLETION;
mb[1] = MSW(stat);
mb[2] = RD_MAILBOX_REG(ha, reg, 2);
- qla2x00_async_event(ha, mb);
+ qla2x00_async_event(vha, rsp, mb);
break;
default:
DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
"(%d).\n",
- ha->host_no, stat & 0xff));
+ vha->host_no, stat & 0xff));
break;
}
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
@@ -223,10 +234,11 @@ qla2300_intr_handler(int irq, void *dev_id)
* @mb0: Mailbox0 register
*/
static void
-qla2x00_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
+qla2x00_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
{
uint16_t cnt;
uint16_t __iomem *wptr;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
/* Load return mailbox registers. */
@@ -247,10 +259,10 @@ qla2x00_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
if (ha->mcp) {
DEBUG3(printk("%s(%ld): Got mailbox completion. cmd=%x.\n",
- __func__, ha->host_no, ha->mcp->mb[0]));
+ __func__, vha->host_no, ha->mcp->mb[0]));
} else {
DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
}
}
@@ -260,7 +272,7 @@ qla2x00_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
* @mb: Mailbox registers (0 - 3)
*/
void
-qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
+qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
{
#define LS_UNKNOWN 2
static char *link_speeds[5] = { "1", "2", "?", "4", "8" };
@@ -268,6 +280,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
uint16_t handle_cnt;
uint16_t cnt;
uint32_t handles[5];
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
uint32_t rscn_entry, host_pid;
uint8_t rscn_queue_index;
@@ -329,17 +342,19 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
switch (mb[0]) {
case MBA_SCSI_COMPLETION: /* Fast Post */
- if (!ha->flags.online)
+ if (!vha->flags.online)
break;
for (cnt = 0; cnt < handle_cnt; cnt++)
- qla2x00_process_completed_request(ha, handles[cnt]);
+ qla2x00_process_completed_request(vha, rsp->req,
+ handles[cnt]);
break;
case MBA_RESET: /* Reset */
- DEBUG2(printk("scsi(%ld): Asynchronous RESET.\n", ha->host_no));
+ DEBUG2(printk("scsi(%ld): Asynchronous RESET.\n",
+ vha->host_no));
- set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+ set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
break;
case MBA_SYSTEM_ERR: /* System Error */
@@ -347,70 +362,70 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
"ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n",
mb[1], mb[2], mb[3]);
- qla2x00_post_hwe_work(ha, mb[0], mb[1], mb[2], mb[3]);
- ha->isp_ops->fw_dump(ha, 1);
+ qla2x00_post_hwe_work(vha, mb[0], mb[1], mb[2], mb[3]);
+ ha->isp_ops->fw_dump(vha, 1);
if (IS_FWI2_CAPABLE(ha)) {
if (mb[1] == 0 && mb[2] == 0) {
qla_printk(KERN_ERR, ha,
"Unrecoverable Hardware Error: adapter "
"marked OFFLINE!\n");
- ha->flags.online = 0;
+ vha->flags.online = 0;
} else
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
} else if (mb[1] == 0) {
qla_printk(KERN_INFO, ha,
"Unrecoverable Hardware Error: adapter marked "
"OFFLINE!\n");
- ha->flags.online = 0;
+ vha->flags.online = 0;
} else
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */
DEBUG2(printk("scsi(%ld): ISP Request Transfer Error.\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n");
- qla2x00_post_hwe_work(ha, mb[0], mb[1], mb[2], mb[3]);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ qla2x00_post_hwe_work(vha, mb[0], mb[1], mb[2], mb[3]);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */
DEBUG2(printk("scsi(%ld): ISP Response Transfer Error.\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha, "ISP Response Transfer Error.\n");
- qla2x00_post_hwe_work(ha, mb[0], mb[1], mb[2], mb[3]);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ qla2x00_post_hwe_work(vha, mb[0], mb[1], mb[2], mb[3]);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
case MBA_WAKEUP_THRES: /* Request Queue Wake-up */
DEBUG2(printk("scsi(%ld): Asynchronous WAKEUP_THRES.\n",
- ha->host_no));
+ vha->host_no));
break;
case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */
- DEBUG2(printk("scsi(%ld): LIP occurred (%x).\n", ha->host_no,
+ DEBUG2(printk("scsi(%ld): LIP occurred (%x).\n", vha->host_no,
mb[1]));
qla_printk(KERN_INFO, ha, "LIP occurred (%x).\n", mb[1]);
- if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
- atomic_set(&ha->loop_state, LOOP_DOWN);
- atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
- qla2x00_mark_all_devices_lost(ha, 1);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(vha, 1);
}
- if (ha->parent) {
- atomic_set(&ha->vp_state, VP_FAILED);
- fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
}
- set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags);
- set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags);
+ set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
- ha->flags.management_server_logged_in = 0;
- qla2x00_post_aen_work(ha, FCH_EVT_LIP, mb[1]);
+ vha->flags.management_server_logged_in = 0;
+ qla2x00_post_aen_work(vha, FCH_EVT_LIP, mb[1]);
break;
case MBA_LOOP_UP: /* Loop Up Event */
@@ -425,59 +440,59 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
}
DEBUG2(printk("scsi(%ld): Asynchronous LOOP UP (%s Gbps).\n",
- ha->host_no, link_speed));
+ vha->host_no, link_speed));
qla_printk(KERN_INFO, ha, "LOOP UP detected (%s Gbps).\n",
link_speed);
- ha->flags.management_server_logged_in = 0;
- qla2x00_post_aen_work(ha, FCH_EVT_LINKUP, ha->link_data_rate);
+ vha->flags.management_server_logged_in = 0;
+ qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha->link_data_rate);
break;
case MBA_LOOP_DOWN: /* Loop Down Event */
DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN "
- "(%x %x %x).\n", ha->host_no, mb[1], mb[2], mb[3]));
+ "(%x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3]));
qla_printk(KERN_INFO, ha, "LOOP DOWN detected (%x %x %x).\n",
mb[1], mb[2], mb[3]);
- if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
- atomic_set(&ha->loop_state, LOOP_DOWN);
- atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
- ha->device_flags |= DFLG_NO_CABLE;
- qla2x00_mark_all_devices_lost(ha, 1);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ vha->device_flags |= DFLG_NO_CABLE;
+ qla2x00_mark_all_devices_lost(vha, 1);
}
- if (ha->parent) {
- atomic_set(&ha->vp_state, VP_FAILED);
- fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
}
- ha->flags.management_server_logged_in = 0;
+ vha->flags.management_server_logged_in = 0;
ha->link_data_rate = PORT_SPEED_UNKNOWN;
- qla2x00_post_aen_work(ha, FCH_EVT_LINKDOWN, 0);
+ qla2x00_post_aen_work(vha, FCH_EVT_LINKDOWN, 0);
break;
case MBA_LIP_RESET: /* LIP reset occurred */
DEBUG2(printk("scsi(%ld): Asynchronous LIP RESET (%x).\n",
- ha->host_no, mb[1]));
+ vha->host_no, mb[1]));
qla_printk(KERN_INFO, ha,
"LIP reset occurred (%x).\n", mb[1]);
- if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
- atomic_set(&ha->loop_state, LOOP_DOWN);
- atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
- qla2x00_mark_all_devices_lost(ha, 1);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(vha, 1);
}
- if (ha->parent) {
- atomic_set(&ha->vp_state, VP_FAILED);
- fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
}
- set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+ set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
ha->operating_mode = LOOP;
- ha->flags.management_server_logged_in = 0;
- qla2x00_post_aen_work(ha, FCH_EVT_LIPRESET, mb[1]);
+ vha->flags.management_server_logged_in = 0;
+ qla2x00_post_aen_work(vha, FCH_EVT_LIPRESET, mb[1]);
break;
case MBA_POINT_TO_POINT: /* Point-to-Point */
@@ -485,33 +500,33 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
break;
DEBUG2(printk("scsi(%ld): Asynchronous P2P MODE received.\n",
- ha->host_no));
+ vha->host_no));
/*
* Until there's a transition from loop down to loop up, treat
* this as loop down only.
*/
- if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
- atomic_set(&ha->loop_state, LOOP_DOWN);
- if (!atomic_read(&ha->loop_down_timer))
- atomic_set(&ha->loop_down_timer,
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ if (!atomic_read(&vha->loop_down_timer))
+ atomic_set(&vha->loop_down_timer,
LOOP_DOWN_TIME);
- qla2x00_mark_all_devices_lost(ha, 1);
+ qla2x00_mark_all_devices_lost(vha, 1);
}
- if (ha->parent) {
- atomic_set(&ha->vp_state, VP_FAILED);
- fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
}
- if (!(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) {
- set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
- }
- set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags);
- set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags);
+ if (!(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)))
+ set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+
+ set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
ha->flags.gpsc_supported = 1;
- ha->flags.management_server_logged_in = 0;
+ vha->flags.management_server_logged_in = 0;
break;
case MBA_CHG_IN_CONNECTION: /* Change in connection mode */
@@ -520,134 +535,137 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
DEBUG2(printk("scsi(%ld): Asynchronous Change In Connection "
"received.\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_INFO, ha,
"Configuration change detected: value=%x.\n", mb[1]);
- if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
- atomic_set(&ha->loop_state, LOOP_DOWN);
- if (!atomic_read(&ha->loop_down_timer))
- atomic_set(&ha->loop_down_timer,
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ if (!atomic_read(&vha->loop_down_timer))
+ atomic_set(&vha->loop_down_timer,
LOOP_DOWN_TIME);
- qla2x00_mark_all_devices_lost(ha, 1);
+ qla2x00_mark_all_devices_lost(vha, 1);
}
- if (ha->parent) {
- atomic_set(&ha->vp_state, VP_FAILED);
- fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
}
- set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
- set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
break;
case MBA_PORT_UPDATE: /* Port database update */
+ /* Only handle SCNs for our Vport index. */
+ if (vha->vp_idx && vha->vp_idx != (mb[3] & 0xff))
+ break;
+
/*
* If PORT UPDATE is global (received LIP_OCCURRED/LIP_RESET
* event etc. earlier indicating loop is down) then process
* it. Otherwise ignore it and Wait for RSCN to come in.
*/
- atomic_set(&ha->loop_down_timer, 0);
- if (atomic_read(&ha->loop_state) != LOOP_DOWN &&
- atomic_read(&ha->loop_state) != LOOP_DEAD) {
+ atomic_set(&vha->loop_down_timer, 0);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN &&
+ atomic_read(&vha->loop_state) != LOOP_DEAD) {
DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE "
- "ignored %04x/%04x/%04x.\n", ha->host_no, mb[1],
+ "ignored %04x/%04x/%04x.\n", vha->host_no, mb[1],
mb[2], mb[3]));
break;
}
DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE.\n",
- ha->host_no));
+ vha->host_no));
DEBUG(printk(KERN_INFO
"scsi(%ld): Port database changed %04x %04x %04x.\n",
- ha->host_no, mb[1], mb[2], mb[3]));
+ vha->host_no, mb[1], mb[2], mb[3]));
/*
* Mark all devices as missing so we will login again.
*/
- atomic_set(&ha->loop_state, LOOP_UP);
+ atomic_set(&vha->loop_state, LOOP_UP);
- qla2x00_mark_all_devices_lost(ha, 1);
+ qla2x00_mark_all_devices_lost(vha, 1);
- ha->flags.rscn_queue_overflow = 1;
+ vha->flags.rscn_queue_overflow = 1;
- set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
- set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
break;
case MBA_RSCN_UPDATE: /* State Change Registration */
/* Check if the Vport has issued a SCR */
- if (ha->parent && test_bit(VP_SCR_NEEDED, &ha->vp_flags))
+ if (vha->vp_idx && test_bit(VP_SCR_NEEDED, &vha->vp_flags))
break;
/* Only handle SCNs for our Vport index. */
- if (ha->parent && ha->vp_idx != (mb[3] & 0xff))
+ if (vha->vp_idx && vha->vp_idx != (mb[3] & 0xff))
break;
-
DEBUG2(printk("scsi(%ld): Asynchronous RSCR UPDATE.\n",
- ha->host_no));
+ vha->host_no));
DEBUG(printk(KERN_INFO
"scsi(%ld): RSCN database changed -- %04x %04x %04x.\n",
- ha->host_no, mb[1], mb[2], mb[3]));
+ vha->host_no, mb[1], mb[2], mb[3]));
rscn_entry = ((mb[1] & 0xff) << 16) | mb[2];
- host_pid = (ha->d_id.b.domain << 16) | (ha->d_id.b.area << 8) |
- ha->d_id.b.al_pa;
+ host_pid = (vha->d_id.b.domain << 16) | (vha->d_id.b.area << 8)
+ | vha->d_id.b.al_pa;
if (rscn_entry == host_pid) {
DEBUG(printk(KERN_INFO
"scsi(%ld): Ignoring RSCN update to local host "
"port ID (%06x)\n",
- ha->host_no, host_pid));
+ vha->host_no, host_pid));
break;
}
/* Ignore reserved bits from RSCN-payload. */
rscn_entry = ((mb[1] & 0x3ff) << 16) | mb[2];
- rscn_queue_index = ha->rscn_in_ptr + 1;
+ rscn_queue_index = vha->rscn_in_ptr + 1;
if (rscn_queue_index == MAX_RSCN_COUNT)
rscn_queue_index = 0;
- if (rscn_queue_index != ha->rscn_out_ptr) {
- ha->rscn_queue[ha->rscn_in_ptr] = rscn_entry;
- ha->rscn_in_ptr = rscn_queue_index;
+ if (rscn_queue_index != vha->rscn_out_ptr) {
+ vha->rscn_queue[vha->rscn_in_ptr] = rscn_entry;
+ vha->rscn_in_ptr = rscn_queue_index;
} else {
- ha->flags.rscn_queue_overflow = 1;
+ vha->flags.rscn_queue_overflow = 1;
}
- atomic_set(&ha->loop_state, LOOP_UPDATE);
- atomic_set(&ha->loop_down_timer, 0);
- ha->flags.management_server_logged_in = 0;
+ atomic_set(&vha->loop_state, LOOP_UPDATE);
+ atomic_set(&vha->loop_down_timer, 0);
+ vha->flags.management_server_logged_in = 0;
- set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
- set_bit(RSCN_UPDATE, &ha->dpc_flags);
- qla2x00_post_aen_work(ha, FCH_EVT_RSCN, rscn_entry);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(RSCN_UPDATE, &vha->dpc_flags);
+ qla2x00_post_aen_work(vha, FCH_EVT_RSCN, rscn_entry);
break;
/* case MBA_RIO_RESPONSE: */
case MBA_ZIO_RESPONSE:
DEBUG2(printk("scsi(%ld): [R|Z]IO update completion.\n",
- ha->host_no));
+ vha->host_no));
DEBUG(printk(KERN_INFO
"scsi(%ld): [R|Z]IO update completion.\n",
- ha->host_no));
+ vha->host_no));
if (IS_FWI2_CAPABLE(ha))
- qla24xx_process_response_queue(ha);
+ qla24xx_process_response_queue(rsp);
else
- qla2x00_process_response_queue(ha);
+ qla2x00_process_response_queue(rsp);
break;
case MBA_DISCARD_RND_FRAME:
DEBUG2(printk("scsi(%ld): Discard RND Frame -- %04x %04x "
- "%04x.\n", ha->host_no, mb[1], mb[2], mb[3]));
+ "%04x.\n", vha->host_no, mb[1], mb[2], mb[3]));
break;
case MBA_TRACE_NOTIFICATION:
DEBUG2(printk("scsi(%ld): Trace Notification -- %04x %04x.\n",
- ha->host_no, mb[1], mb[2]));
+ vha->host_no, mb[1], mb[2]));
break;
case MBA_ISP84XX_ALERT:
DEBUG2(printk("scsi(%ld): ISP84XX Alert Notification -- "
- "%04x %04x %04x\n", ha->host_no, mb[1], mb[2], mb[3]));
+ "%04x %04x %04x\n", vha->host_no, mb[1], mb[2], mb[3]));
spin_lock_irqsave(&ha->cs84xx->access_lock, flags);
switch (mb[1]) {
@@ -682,16 +700,22 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
break;
}
- if (!ha->parent && ha->num_vhosts)
- qla2x00_alert_all_vps(ha, mb);
+ if (!vha->vp_idx && ha->num_vhosts)
+ qla2x00_alert_all_vps(rsp, mb);
}
static void
qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, void *data)
{
fc_port_t *fcport = data;
+ struct scsi_qla_host *vha = fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = NULL;
- if (fcport->ha->max_q_depth <= sdev->queue_depth)
+ req = ha->req_q_map[vha->req_ques[0]];
+ if (!req)
+ return;
+ if (req->max_q_depth <= sdev->queue_depth)
return;
if (sdev->ordered_tags)
@@ -703,9 +727,9 @@ qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, void *data)
fcport->last_ramp_up = jiffies;
- DEBUG2(qla_printk(KERN_INFO, fcport->ha,
+ DEBUG2(qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d:%d): Queue depth adjusted-up to %d.\n",
- fcport->ha->host_no, sdev->channel, sdev->id, sdev->lun,
+ fcport->vha->host_no, sdev->channel, sdev->id, sdev->lun,
sdev->queue_depth));
}
@@ -717,20 +741,21 @@ qla2x00_adjust_sdev_qdepth_down(struct scsi_device *sdev, void *data)
if (!scsi_track_queue_full(sdev, sdev->queue_depth - 1))
return;
- DEBUG2(qla_printk(KERN_INFO, fcport->ha,
+ DEBUG2(qla_printk(KERN_INFO, fcport->vha->hw,
"scsi(%ld:%d:%d:%d): Queue depth adjusted-down to %d.\n",
- fcport->ha->host_no, sdev->channel, sdev->id, sdev->lun,
+ fcport->vha->host_no, sdev->channel, sdev->id, sdev->lun,
sdev->queue_depth));
}
static inline void
-qla2x00_ramp_up_queue_depth(scsi_qla_host_t *ha, srb_t *sp)
+qla2x00_ramp_up_queue_depth(scsi_qla_host_t *vha, struct req_que *req,
+ srb_t *sp)
{
fc_port_t *fcport;
struct scsi_device *sdev;
sdev = sp->cmd->device;
- if (sdev->queue_depth >= ha->max_q_depth)
+ if (sdev->queue_depth >= req->max_q_depth)
return;
fcport = sp->fcport;
@@ -751,25 +776,27 @@ qla2x00_ramp_up_queue_depth(scsi_qla_host_t *ha, srb_t *sp)
* @index: SRB index
*/
static void
-qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index)
+qla2x00_process_completed_request(struct scsi_qla_host *vha,
+ struct req_que *req, uint32_t index)
{
srb_t *sp;
+ struct qla_hw_data *ha = vha->hw;
/* Validate handle. */
if (index >= MAX_OUTSTANDING_COMMANDS) {
DEBUG2(printk("scsi(%ld): Invalid SCSI completion handle %d.\n",
- ha->host_no, index));
+ vha->host_no, index));
qla_printk(KERN_WARNING, ha,
"Invalid SCSI completion handle %d.\n", index);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
return;
}
- sp = ha->outstanding_cmds[index];
+ sp = req->outstanding_cmds[index];
if (sp) {
/* Free outstanding command slot. */
- ha->outstanding_cmds[index] = NULL;
+ req->outstanding_cmds[index] = NULL;
CMD_COMPL_STATUS(sp->cmd) = 0L;
CMD_SCSI_STATUS(sp->cmd) = 0L;
@@ -777,15 +804,15 @@ qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index)
/* Save ISP completion status */
sp->cmd->result = DID_OK << 16;
- qla2x00_ramp_up_queue_depth(ha, sp);
+ qla2x00_ramp_up_queue_depth(vha, req, sp);
qla2x00_sp_compl(ha, sp);
} else {
DEBUG2(printk("scsi(%ld): Invalid ISP SCSI completion handle\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha,
"Invalid ISP SCSI completion handle\n");
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
}
}
@@ -794,32 +821,36 @@ qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index)
* @ha: SCSI driver HA context
*/
void
-qla2x00_process_response_queue(struct scsi_qla_host *ha)
+qla2x00_process_response_queue(struct rsp_que *rsp)
{
+ struct scsi_qla_host *vha;
+ struct qla_hw_data *ha = rsp->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
sts_entry_t *pkt;
uint16_t handle_cnt;
uint16_t cnt;
- if (!ha->flags.online)
+ vha = qla2x00_get_rsp_host(rsp);
+
+ if (!vha->flags.online)
return;
- while (ha->response_ring_ptr->signature != RESPONSE_PROCESSED) {
- pkt = (sts_entry_t *)ha->response_ring_ptr;
+ while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
+ pkt = (sts_entry_t *)rsp->ring_ptr;
- ha->rsp_ring_index++;
- if (ha->rsp_ring_index == ha->response_q_length) {
- ha->rsp_ring_index = 0;
- ha->response_ring_ptr = ha->response_ring;
+ rsp->ring_index++;
+ if (rsp->ring_index == rsp->length) {
+ rsp->ring_index = 0;
+ rsp->ring_ptr = rsp->ring;
} else {
- ha->response_ring_ptr++;
+ rsp->ring_ptr++;
}
if (pkt->entry_status != 0) {
DEBUG3(printk(KERN_INFO
- "scsi(%ld): Process error entry.\n", ha->host_no));
+ "scsi(%ld): Process error entry.\n", vha->host_no));
- qla2x00_error_entry(ha, pkt);
+ qla2x00_error_entry(vha, rsp, pkt);
((response_t *)pkt)->signature = RESPONSE_PROCESSED;
wmb();
continue;
@@ -827,31 +858,31 @@ qla2x00_process_response_queue(struct scsi_qla_host *ha)
switch (pkt->entry_type) {
case STATUS_TYPE:
- qla2x00_status_entry(ha, pkt);
+ qla2x00_status_entry(vha, rsp, pkt);
break;
case STATUS_TYPE_21:
handle_cnt = ((sts21_entry_t *)pkt)->handle_count;
for (cnt = 0; cnt < handle_cnt; cnt++) {
- qla2x00_process_completed_request(ha,
+ qla2x00_process_completed_request(vha, rsp->req,
((sts21_entry_t *)pkt)->handle[cnt]);
}
break;
case STATUS_TYPE_22:
handle_cnt = ((sts22_entry_t *)pkt)->handle_count;
for (cnt = 0; cnt < handle_cnt; cnt++) {
- qla2x00_process_completed_request(ha,
+ qla2x00_process_completed_request(vha, rsp->req,
((sts22_entry_t *)pkt)->handle[cnt]);
}
break;
case STATUS_CONT_TYPE:
- qla2x00_status_cont_entry(ha, (sts_cont_entry_t *)pkt);
+ qla2x00_status_cont_entry(vha, (sts_cont_entry_t *)pkt);
break;
default:
/* Type Not Supported. */
DEBUG4(printk(KERN_WARNING
"scsi(%ld): Received unknown response pkt type %x "
"entry status=%x.\n",
- ha->host_no, pkt->entry_type, pkt->entry_status));
+ vha->host_no, pkt->entry_type, pkt->entry_status));
break;
}
((response_t *)pkt)->signature = RESPONSE_PROCESSED;
@@ -859,7 +890,7 @@ qla2x00_process_response_queue(struct scsi_qla_host *ha)
}
/* Adjust ring index */
- WRT_REG_WORD(ISP_RSP_Q_OUT(ha, reg), ha->rsp_ring_index);
+ WRT_REG_WORD(ISP_RSP_Q_OUT(ha, reg), rsp->ring_index);
}
static inline void
@@ -881,10 +912,10 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len)
sp->request_sense_ptr += sense_len;
sp->request_sense_length -= sense_len;
if (sp->request_sense_length != 0)
- sp->fcport->ha->status_srb = sp;
+ sp->fcport->vha->status_srb = sp;
DEBUG5(printk("%s(): Check condition Sense data, scsi(%ld:%d:%d:%d) "
- "cmd=%p pid=%ld\n", __func__, sp->fcport->ha->host_no,
+ "cmd=%p pid=%ld\n", __func__, sp->fcport->vha->host_no,
cp->device->channel, cp->device->id, cp->device->lun, cp,
cp->serial_number));
if (sense_len)
@@ -898,7 +929,7 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len)
* @pkt: Entry pointer
*/
static void
-qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
+qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
{
srb_t *sp;
fc_port_t *fcport;
@@ -911,6 +942,8 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
int32_t resid;
uint32_t sense_len, rsp_info_len, resid_len, fw_resid_len;
uint8_t *rsp_info, *sense_data;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = rsp->req;
sts = (sts_entry_t *) pkt;
sts24 = (struct sts_entry_24xx *) pkt;
@@ -924,31 +957,31 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
/* Fast path completion. */
if (comp_status == CS_COMPLETE && scsi_status == 0) {
- qla2x00_process_completed_request(ha, sts->handle);
+ qla2x00_process_completed_request(vha, req, sts->handle);
return;
}
/* Validate handle. */
if (sts->handle < MAX_OUTSTANDING_COMMANDS) {
- sp = ha->outstanding_cmds[sts->handle];
- ha->outstanding_cmds[sts->handle] = NULL;
+ sp = req->outstanding_cmds[sts->handle];
+ req->outstanding_cmds[sts->handle] = NULL;
} else
sp = NULL;
if (sp == NULL) {
DEBUG2(printk("scsi(%ld): Status Entry invalid handle.\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha, "Status Entry invalid handle.\n");
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
- qla2xxx_wake_dpc(ha);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
return;
}
cp = sp->cmd;
if (cp == NULL) {
DEBUG2(printk("scsi(%ld): Command already returned back to OS "
- "pkt->handle=%d sp=%p.\n", ha->host_no, sts->handle, sp));
+ "pkt->handle=%d sp=%p.\n", vha->host_no, sts->handle, sp));
qla_printk(KERN_WARNING, ha,
"Command is NULL: already returned to OS (sp=%p)\n", sp);
@@ -987,7 +1020,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if (rsp_info_len > 3 && rsp_info[3]) {
DEBUG2(printk("scsi(%ld:%d:%d:%d) FCP I/O protocol "
"failure (%x/%02x%02x%02x%02x%02x%02x%02x%02x)..."
- "retrying command\n", ha->host_no,
+ "retrying command\n", vha->host_no,
cp->device->channel, cp->device->id,
cp->device->lun, rsp_info_len, rsp_info[0],
rsp_info[1], rsp_info[2], rsp_info[3], rsp_info[4],
@@ -1025,7 +1058,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d:%d): Mid-layer underflow "
"detected (%x of %x bytes)...returning "
- "error status.\n", ha->host_no,
+ "error status.\n", vha->host_no,
cp->device->channel, cp->device->id,
cp->device->lun, resid,
scsi_bufflen(cp));
@@ -1039,7 +1072,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
DEBUG2(printk(KERN_INFO
"scsi(%ld): QUEUE FULL status detected "
- "0x%x-0x%x.\n", ha->host_no, comp_status,
+ "0x%x-0x%x.\n", vha->host_no, comp_status,
scsi_status));
/* Adjust queue depth for all luns on the port. */
@@ -1078,7 +1111,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d) UNDERRUN status detected "
"0x%x-0x%x. resid=0x%x fw_resid=0x%x cdb=0x%x "
- "os_underflow=0x%x\n", ha->host_no,
+ "os_underflow=0x%x\n", vha->host_no,
cp->device->id, cp->device->lun, comp_status,
scsi_status, resid_len, resid, cp->cmnd[0],
cp->underflow));
@@ -1095,7 +1128,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
DEBUG2(printk(KERN_INFO
"scsi(%ld): QUEUE FULL status detected "
- "0x%x-0x%x.\n", ha->host_no, comp_status,
+ "0x%x-0x%x.\n", vha->host_no, comp_status,
scsi_status));
/*
@@ -1125,10 +1158,10 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if (!(scsi_status & SS_RESIDUAL_UNDER)) {
DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped "
"frame(s) detected (%x of %x bytes)..."
- "retrying command.\n", ha->host_no,
- cp->device->channel, cp->device->id,
- cp->device->lun, resid,
- scsi_bufflen(cp)));
+ "retrying command.\n",
+ vha->host_no, cp->device->channel,
+ cp->device->id, cp->device->lun, resid,
+ scsi_bufflen(cp)));
cp->result = DID_BUS_BUSY << 16;
break;
@@ -1140,7 +1173,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d:%d): Mid-layer underflow "
"detected (%x of %x bytes)...returning "
- "error status.\n", ha->host_no,
+ "error status.\n", vha->host_no,
cp->device->channel, cp->device->id,
cp->device->lun, resid,
scsi_bufflen(cp));
@@ -1157,7 +1190,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
case CS_DATA_OVERRUN:
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d): OVERRUN status detected 0x%x-0x%x\n",
- ha->host_no, cp->device->id, cp->device->lun, comp_status,
+ vha->host_no, cp->device->id, cp->device->lun, comp_status,
scsi_status));
DEBUG2(printk(KERN_INFO
"CDB: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
@@ -1183,7 +1216,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
*/
DEBUG2(printk("scsi(%ld:%d:%d): status_entry: Port Down "
"pid=%ld, compl status=0x%x, port state=0x%x\n",
- ha->host_no, cp->device->id, cp->device->lun,
+ vha->host_no, cp->device->id, cp->device->lun,
cp->serial_number, comp_status,
atomic_read(&fcport->state)));
@@ -1194,13 +1227,13 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
*/
cp->result = DID_TRANSPORT_DISRUPTED << 16;
if (atomic_read(&fcport->state) == FCS_ONLINE)
- qla2x00_mark_device_lost(fcport->ha, fcport, 1, 1);
+ qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1);
break;
case CS_RESET:
DEBUG2(printk(KERN_INFO
"scsi(%ld): RESET status detected 0x%x-0x%x.\n",
- ha->host_no, comp_status, scsi_status));
+ vha->host_no, comp_status, scsi_status));
cp->result = DID_RESET << 16;
break;
@@ -1213,7 +1246,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
*/
DEBUG2(printk(KERN_INFO
"scsi(%ld): ABORT status detected 0x%x-0x%x.\n",
- ha->host_no, comp_status, scsi_status));
+ vha->host_no, comp_status, scsi_status));
cp->result = DID_RESET << 16;
break;
@@ -1229,25 +1262,25 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if (IS_FWI2_CAPABLE(ha)) {
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d:%d): TIMEOUT status detected "
- "0x%x-0x%x\n", ha->host_no, cp->device->channel,
+ "0x%x-0x%x\n", vha->host_no, cp->device->channel,
cp->device->id, cp->device->lun, comp_status,
scsi_status));
break;
}
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d:%d): TIMEOUT status detected 0x%x-0x%x "
- "sflags=%x.\n", ha->host_no, cp->device->channel,
+ "sflags=%x.\n", vha->host_no, cp->device->channel,
cp->device->id, cp->device->lun, comp_status, scsi_status,
le16_to_cpu(sts->status_flags)));
/* Check to see if logout occurred. */
if ((le16_to_cpu(sts->status_flags) & SF_LOGOUT_SENT))
- qla2x00_mark_device_lost(fcport->ha, fcport, 1, 1);
+ qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1);
break;
default:
DEBUG3(printk("scsi(%ld): Error detected (unknown status) "
- "0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status));
+ "0x%x-0x%x.\n", vha->host_no, comp_status, scsi_status));
qla_printk(KERN_INFO, ha,
"Unknown status detected 0x%x-0x%x.\n",
comp_status, scsi_status);
@@ -1257,7 +1290,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
}
/* Place command on done queue. */
- if (ha->status_srb == NULL)
+ if (vha->status_srb == NULL)
qla2x00_sp_compl(ha, sp);
}
@@ -1269,10 +1302,11 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
* Extended sense data.
*/
static void
-qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt)
+qla2x00_status_cont_entry(scsi_qla_host_t *vha, sts_cont_entry_t *pkt)
{
uint8_t sense_sz = 0;
- srb_t *sp = ha->status_srb;
+ struct qla_hw_data *ha = vha->hw;
+ srb_t *sp = vha->status_srb;
struct scsi_cmnd *cp;
if (sp != NULL && sp->request_sense_length != 0) {
@@ -1284,7 +1318,7 @@ qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt)
"cmd is NULL: already returned to OS (sp=%p)\n",
sp);
- ha->status_srb = NULL;
+ vha->status_srb = NULL;
return;
}
@@ -1305,7 +1339,7 @@ qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt)
/* Place command on done queue. */
if (sp->request_sense_length == 0) {
- ha->status_srb = NULL;
+ vha->status_srb = NULL;
qla2x00_sp_compl(ha, sp);
}
}
@@ -1317,10 +1351,11 @@ qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt)
* @pkt: Entry pointer
*/
static void
-qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt)
+qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
{
srb_t *sp;
-
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = rsp->req;
#if defined(QL_DEBUG_LEVEL_2)
if (pkt->entry_status & RF_INV_E_ORDER)
qla_printk(KERN_ERR, ha, "%s: Invalid Entry Order\n", __func__);
@@ -1339,13 +1374,13 @@ qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt)
/* Validate handle. */
if (pkt->handle < MAX_OUTSTANDING_COMMANDS)
- sp = ha->outstanding_cmds[pkt->handle];
+ sp = req->outstanding_cmds[pkt->handle];
else
sp = NULL;
if (sp) {
/* Free outstanding command slot. */
- ha->outstanding_cmds[pkt->handle] = NULL;
+ req->outstanding_cmds[pkt->handle] = NULL;
/* Bad payload or header */
if (pkt->entry_status &
@@ -1362,12 +1397,12 @@ qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt)
} else if (pkt->entry_type == COMMAND_A64_TYPE || pkt->entry_type ==
COMMAND_TYPE || pkt->entry_type == COMMAND_TYPE_7) {
DEBUG2(printk("scsi(%ld): Error entry - invalid handle\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha,
"Error entry - invalid handle\n");
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
- qla2xxx_wake_dpc(ha);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
}
}
@@ -1377,10 +1412,11 @@ qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt)
* @mb0: Mailbox0 register
*/
static void
-qla24xx_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
+qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
{
uint16_t cnt;
uint16_t __iomem *wptr;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
/* Load return mailbox registers. */
@@ -1395,10 +1431,10 @@ qla24xx_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
if (ha->mcp) {
DEBUG3(printk("%s(%ld): Got mailbox completion. cmd=%x.\n",
- __func__, ha->host_no, ha->mcp->mb[0]));
+ __func__, vha->host_no, ha->mcp->mb[0]));
} else {
DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
}
}
@@ -1407,30 +1443,33 @@ qla24xx_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
* @ha: SCSI driver HA context
*/
void
-qla24xx_process_response_queue(struct scsi_qla_host *ha)
+qla24xx_process_response_queue(struct rsp_que *rsp)
{
- struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ struct qla_hw_data *ha = rsp->hw;
struct sts_entry_24xx *pkt;
+ struct scsi_qla_host *vha;
+
+ vha = qla2x00_get_rsp_host(rsp);
- if (!ha->flags.online)
+ if (!vha->flags.online)
return;
- while (ha->response_ring_ptr->signature != RESPONSE_PROCESSED) {
- pkt = (struct sts_entry_24xx *)ha->response_ring_ptr;
+ while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
+ pkt = (struct sts_entry_24xx *)rsp->ring_ptr;
- ha->rsp_ring_index++;
- if (ha->rsp_ring_index == ha->response_q_length) {
- ha->rsp_ring_index = 0;
- ha->response_ring_ptr = ha->response_ring;
+ rsp->ring_index++;
+ if (rsp->ring_index == rsp->length) {
+ rsp->ring_index = 0;
+ rsp->ring_ptr = rsp->ring;
} else {
- ha->response_ring_ptr++;
+ rsp->ring_ptr++;
}
if (pkt->entry_status != 0) {
DEBUG3(printk(KERN_INFO
- "scsi(%ld): Process error entry.\n", ha->host_no));
+ "scsi(%ld): Process error entry.\n", vha->host_no));
- qla2x00_error_entry(ha, (sts_entry_t *) pkt);
+ qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt);
((response_t *)pkt)->signature = RESPONSE_PROCESSED;
wmb();
continue;
@@ -1438,13 +1477,13 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha)
switch (pkt->entry_type) {
case STATUS_TYPE:
- qla2x00_status_entry(ha, pkt);
+ qla2x00_status_entry(vha, rsp, pkt);
break;
case STATUS_CONT_TYPE:
- qla2x00_status_cont_entry(ha, (sts_cont_entry_t *)pkt);
+ qla2x00_status_cont_entry(vha, (sts_cont_entry_t *)pkt);
break;
case VP_RPT_ID_IOCB_TYPE:
- qla24xx_report_id_acquisition(ha,
+ qla24xx_report_id_acquisition(vha,
(struct vp_rpt_id_entry_24xx *)pkt);
break;
default:
@@ -1452,7 +1491,7 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha)
DEBUG4(printk(KERN_WARNING
"scsi(%ld): Received unknown response pkt type %x "
"entry status=%x.\n",
- ha->host_no, pkt->entry_type, pkt->entry_status));
+ vha->host_no, pkt->entry_type, pkt->entry_status));
break;
}
((response_t *)pkt)->signature = RESPONSE_PROCESSED;
@@ -1460,14 +1499,15 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha)
}
/* Adjust ring index */
- WRT_REG_DWORD(&reg->rsp_q_out, ha->rsp_ring_index);
+ ha->isp_ops->wrt_rsp_reg(ha, rsp->id, rsp->ring_index);
}
static void
-qla2xxx_check_risc_status(scsi_qla_host_t *ha)
+qla2xxx_check_risc_status(scsi_qla_host_t *vha)
{
int rval;
uint32_t cnt;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
if (!IS_QLA25XX(ha))
@@ -1521,25 +1561,29 @@ done:
irqreturn_t
qla24xx_intr_handler(int irq, void *dev_id)
{
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
struct device_reg_24xx __iomem *reg;
int status;
unsigned long iter;
uint32_t stat;
uint32_t hccr;
uint16_t mb[4];
+ struct rsp_que *rsp;
- ha = (scsi_qla_host_t *) dev_id;
- if (!ha) {
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
printk(KERN_INFO
- "%s(): NULL host pointer\n", __func__);
+ "%s(): NULL response queue pointer\n", __func__);
return IRQ_NONE;
}
+ ha = rsp->hw;
reg = &ha->iobase->isp24;
status = 0;
spin_lock(&ha->hardware_lock);
+ vha = qla2x00_get_rsp_host(rsp);
for (iter = 50; iter--; ) {
stat = RD_REG_DWORD(&reg->host_status);
if (stat & HSRX_RISC_PAUSED) {
@@ -1547,7 +1591,7 @@ qla24xx_intr_handler(int irq, void *dev_id)
break;
if (ha->hw_event_pause_errors == 0)
- qla2x00_post_hwe_work(ha, HW_EVENT_PARITY_ERR,
+ qla2x00_post_hwe_work(vha, HW_EVENT_PARITY_ERR,
0, MSW(stat), LSW(stat));
else if (ha->hw_event_pause_errors < 0xffffffff)
ha->hw_event_pause_errors++;
@@ -1557,10 +1601,10 @@ qla24xx_intr_handler(int irq, void *dev_id)
qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, "
"Dumping firmware!\n", hccr);
- qla2xxx_check_risc_status(ha);
+ qla2xxx_check_risc_status(vha);
- ha->isp_ops->fw_dump(ha, 1);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ ha->isp_ops->fw_dump(vha, 1);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
} else if ((stat & HSRX_RISC_INT) == 0)
break;
@@ -1570,7 +1614,7 @@ qla24xx_intr_handler(int irq, void *dev_id)
case 0x2:
case 0x10:
case 0x11:
- qla24xx_mbx_completion(ha, MSW(stat));
+ qla24xx_mbx_completion(vha, MSW(stat));
status |= MBX_INTERRUPT;
break;
@@ -1579,15 +1623,16 @@ qla24xx_intr_handler(int irq, void *dev_id)
mb[1] = RD_REG_WORD(&reg->mailbox1);
mb[2] = RD_REG_WORD(&reg->mailbox2);
mb[3] = RD_REG_WORD(&reg->mailbox3);
- qla2x00_async_event(ha, mb);
+ qla2x00_async_event(vha, rsp, mb);
break;
case 0x13:
- qla24xx_process_response_queue(ha);
+ case 0x14:
+ qla24xx_process_response_queue(rsp);
break;
default:
DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
"(%d).\n",
- ha->host_no, stat & 0xff));
+ vha->host_no, stat & 0xff));
break;
}
WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
@@ -1607,15 +1652,22 @@ qla24xx_intr_handler(int irq, void *dev_id)
static irqreturn_t
qla24xx_msix_rsp_q(int irq, void *dev_id)
{
- scsi_qla_host_t *ha;
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
struct device_reg_24xx __iomem *reg;
- ha = dev_id;
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ printk(KERN_INFO
+ "%s(): NULL response queue pointer\n", __func__);
+ return IRQ_NONE;
+ }
+ ha = rsp->hw;
reg = &ha->iobase->isp24;
spin_lock_irq(&ha->hardware_lock);
- qla24xx_process_response_queue(ha);
+ qla24xx_process_response_queue(rsp);
WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
spin_unlock_irq(&ha->hardware_lock);
@@ -1624,20 +1676,64 @@ qla24xx_msix_rsp_q(int irq, void *dev_id)
}
static irqreturn_t
+qla25xx_msix_rsp_q(int irq, void *dev_id)
+{
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
+ struct device_reg_24xx __iomem *reg;
+ uint16_t msix_disabled_hccr = 0;
+
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ printk(KERN_INFO
+ "%s(): NULL response queue pointer\n", __func__);
+ return IRQ_NONE;
+ }
+ ha = rsp->hw;
+ reg = &ha->iobase->isp24;
+
+ spin_lock_irq(&ha->hardware_lock);
+
+ msix_disabled_hccr = rsp->options;
+ if (!rsp->id)
+ msix_disabled_hccr &= __constant_cpu_to_le32(BIT_22);
+ else
+ msix_disabled_hccr &= __constant_cpu_to_le32(BIT_6);
+
+ qla24xx_process_response_queue(rsp);
+
+ if (!msix_disabled_hccr)
+ WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
+
+ spin_unlock_irq(&ha->hardware_lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
qla24xx_msix_default(int irq, void *dev_id)
{
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
struct device_reg_24xx __iomem *reg;
int status;
uint32_t stat;
uint32_t hccr;
uint16_t mb[4];
- ha = dev_id;
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ DEBUG(printk(
+ "%s(): NULL response queue pointer\n", __func__));
+ return IRQ_NONE;
+ }
+ ha = rsp->hw;
reg = &ha->iobase->isp24;
status = 0;
spin_lock_irq(&ha->hardware_lock);
+ vha = qla2x00_get_rsp_host(rsp);
do {
stat = RD_REG_DWORD(&reg->host_status);
if (stat & HSRX_RISC_PAUSED) {
@@ -1645,7 +1741,7 @@ qla24xx_msix_default(int irq, void *dev_id)
break;
if (ha->hw_event_pause_errors == 0)
- qla2x00_post_hwe_work(ha, HW_EVENT_PARITY_ERR,
+ qla2x00_post_hwe_work(vha, HW_EVENT_PARITY_ERR,
0, MSW(stat), LSW(stat));
else if (ha->hw_event_pause_errors < 0xffffffff)
ha->hw_event_pause_errors++;
@@ -1655,10 +1751,10 @@ qla24xx_msix_default(int irq, void *dev_id)
qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, "
"Dumping firmware!\n", hccr);
- qla2xxx_check_risc_status(ha);
+ qla2xxx_check_risc_status(vha);
- ha->isp_ops->fw_dump(ha, 1);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ ha->isp_ops->fw_dump(vha, 1);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
} else if ((stat & HSRX_RISC_INT) == 0)
break;
@@ -1668,7 +1764,7 @@ qla24xx_msix_default(int irq, void *dev_id)
case 0x2:
case 0x10:
case 0x11:
- qla24xx_mbx_completion(ha, MSW(stat));
+ qla24xx_mbx_completion(vha, MSW(stat));
status |= MBX_INTERRUPT;
break;
@@ -1677,15 +1773,16 @@ qla24xx_msix_default(int irq, void *dev_id)
mb[1] = RD_REG_WORD(&reg->mailbox1);
mb[2] = RD_REG_WORD(&reg->mailbox2);
mb[3] = RD_REG_WORD(&reg->mailbox3);
- qla2x00_async_event(ha, mb);
+ qla2x00_async_event(vha, rsp, mb);
break;
case 0x13:
- qla24xx_process_response_queue(ha);
+ case 0x14:
+ qla24xx_process_response_queue(rsp);
break;
default:
DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
"(%d).\n",
- ha->host_no, stat & 0xff));
+ vha->host_no, stat & 0xff));
break;
}
WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
@@ -1710,70 +1807,138 @@ struct qla_init_msix_entry {
irq_handler_t handler;
};
-static struct qla_init_msix_entry imsix_entries[QLA_MSIX_ENTRIES] = {
- { QLA_MSIX_DEFAULT, QLA_MIDX_DEFAULT,
- "qla2xxx (default)", qla24xx_msix_default },
+static struct qla_init_msix_entry base_queue = {
+ .entry = 0,
+ .index = 0,
+ .name = "qla2xxx (default)",
+ .handler = qla24xx_msix_default,
+};
+
+static struct qla_init_msix_entry base_rsp_queue = {
+ .entry = 1,
+ .index = 1,
+ .name = "qla2xxx (rsp_q)",
+ .handler = qla24xx_msix_rsp_q,
+};
- { QLA_MSIX_RSP_Q, QLA_MIDX_RSP_Q,
- "qla2xxx (rsp_q)", qla24xx_msix_rsp_q },
+static struct qla_init_msix_entry multi_rsp_queue = {
+ .entry = 1,
+ .index = 1,
+ .name = "qla2xxx (multi_q)",
+ .handler = qla25xx_msix_rsp_q,
};
static void
-qla24xx_disable_msix(scsi_qla_host_t *ha)
+qla24xx_disable_msix(struct qla_hw_data *ha)
{
int i;
struct qla_msix_entry *qentry;
- for (i = 0; i < QLA_MSIX_ENTRIES; i++) {
- qentry = &ha->msix_entries[imsix_entries[i].index];
+ for (i = 0; i < ha->msix_count; i++) {
+ qentry = &ha->msix_entries[i];
if (qentry->have_irq)
- free_irq(qentry->msix_vector, ha);
+ free_irq(qentry->vector, qentry->rsp);
}
pci_disable_msix(ha->pdev);
+ kfree(ha->msix_entries);
+ ha->msix_entries = NULL;
+ ha->flags.msix_enabled = 0;
}
static int
-qla24xx_enable_msix(scsi_qla_host_t *ha)
+qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
{
int i, ret;
- struct msix_entry entries[QLA_MSIX_ENTRIES];
+ struct msix_entry *entries;
struct qla_msix_entry *qentry;
+ struct qla_init_msix_entry *msix_queue;
- for (i = 0; i < QLA_MSIX_ENTRIES; i++)
- entries[i].entry = imsix_entries[i].entry;
+ entries = kzalloc(sizeof(struct msix_entry) * ha->msix_count,
+ GFP_KERNEL);
+ if (!entries)
+ return -ENOMEM;
- ret = pci_enable_msix(ha->pdev, entries, ARRAY_SIZE(entries));
+ for (i = 0; i < ha->msix_count; i++)
+ entries[i].entry = i;
+
+ ret = pci_enable_msix(ha->pdev, entries, ha->msix_count);
if (ret) {
qla_printk(KERN_WARNING, ha,
- "MSI-X: Failed to enable support -- %d/%d\n",
- QLA_MSIX_ENTRIES, ret);
+ "MSI-X: Failed to enable support -- %d/%d\n"
+ " Retry with %d vectors\n", ha->msix_count, ret, ret);
+ ha->msix_count = ret;
+ ret = pci_enable_msix(ha->pdev, entries, ha->msix_count);
+ if (ret) {
+ qla_printk(KERN_WARNING, ha, "MSI-X: Failed to enable"
+ " support, giving up -- %d/%d\n",
+ ha->msix_count, ret);
+ goto msix_out;
+ }
+ ha->max_queues = ha->msix_count - 1;
+ }
+ ha->msix_entries = kzalloc(sizeof(struct qla_msix_entry) *
+ ha->msix_count, GFP_KERNEL);
+ if (!ha->msix_entries) {
+ ret = -ENOMEM;
goto msix_out;
}
ha->flags.msix_enabled = 1;
- for (i = 0; i < QLA_MSIX_ENTRIES; i++) {
- qentry = &ha->msix_entries[imsix_entries[i].index];
- qentry->msix_vector = entries[i].vector;
- qentry->msix_entry = entries[i].entry;
+ for (i = 0; i < ha->msix_count; i++) {
+ qentry = &ha->msix_entries[i];
+ qentry->vector = entries[i].vector;
+ qentry->entry = entries[i].entry;
qentry->have_irq = 0;
- ret = request_irq(qentry->msix_vector,
- imsix_entries[i].handler, 0, imsix_entries[i].name, ha);
- if (ret) {
- qla_printk(KERN_WARNING, ha,
- "MSI-X: Unable to register handler -- %x/%d.\n",
- imsix_entries[i].index, ret);
- qla24xx_disable_msix(ha);
- goto msix_out;
- }
- qentry->have_irq = 1;
+ qentry->rsp = NULL;
+ }
+
+ /* Enable MSI-X for AENs for queue 0 */
+ qentry = &ha->msix_entries[0];
+ ret = request_irq(qentry->vector, base_queue.handler, 0,
+ base_queue.name, rsp);
+ if (ret) {
+ qla_printk(KERN_WARNING, ha,
+ "MSI-X: Unable to register handler -- %x/%d.\n",
+ qentry->vector, ret);
+ qla24xx_disable_msix(ha);
+ goto msix_out;
}
+ qentry->have_irq = 1;
+ qentry->rsp = rsp;
+
+ /* Enable MSI-X vector for response queue update for queue 0 */
+ if (ha->max_queues > 1 && ha->mqiobase) {
+ ha->mqenable = 1;
+ msix_queue = &multi_rsp_queue;
+ qla_printk(KERN_INFO, ha,
+ "MQ enabled, Number of Queue Resources: %d \n",
+ ha->max_queues);
+ } else {
+ ha->mqenable = 0;
+ msix_queue = &base_rsp_queue;
+ }
+
+ qentry = &ha->msix_entries[1];
+ ret = request_irq(qentry->vector, msix_queue->handler, 0,
+ msix_queue->name, rsp);
+ if (ret) {
+ qla_printk(KERN_WARNING, ha,
+ "MSI-X: Unable to register handler -- %x/%d.\n",
+ qentry->vector, ret);
+ qla24xx_disable_msix(ha);
+ ha->mqenable = 0;
+ goto msix_out;
+ }
+ qentry->have_irq = 1;
+ qentry->rsp = rsp;
msix_out:
+ kfree(entries);
return ret;
}
int
-qla2x00_request_irqs(scsi_qla_host_t *ha)
+qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp)
{
int ret;
device_reg_t __iomem *reg = ha->iobase;
@@ -1782,11 +1947,11 @@ qla2x00_request_irqs(scsi_qla_host_t *ha)
if (!IS_QLA2432(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha))
goto skip_msix;
- if (IS_QLA2432(ha) && (ha->chip_revision < QLA_MSIX_CHIP_REV_24XX ||
- !QLA_MSIX_FW_MODE_1(ha->fw_attributes))) {
+ if (IS_QLA2432(ha) && (ha->pdev->revision < QLA_MSIX_CHIP_REV_24XX ||
+ !QLA_MSIX_FW_MODE_1(ha->fw_attributes))) {
DEBUG2(qla_printk(KERN_WARNING, ha,
- "MSI-X: Unsupported ISP2432 (0x%X, 0x%X).\n",
- ha->chip_revision, ha->fw_attributes));
+ "MSI-X: Unsupported ISP2432 (0x%X, 0x%X).\n",
+ ha->pdev->revision, ha->fw_attributes));
goto skip_msix;
}
@@ -1803,7 +1968,7 @@ qla2x00_request_irqs(scsi_qla_host_t *ha)
goto skip_msi;
}
- ret = qla24xx_enable_msix(ha);
+ ret = qla24xx_enable_msix(ha, rsp);
if (!ret) {
DEBUG2(qla_printk(KERN_INFO, ha,
"MSI-X: Enabled (0x%X, 0x%X).\n", ha->chip_revision,
@@ -1825,7 +1990,7 @@ skip_msix:
skip_msi:
ret = request_irq(ha->pdev->irq, ha->isp_ops->intr_handler,
- IRQF_DISABLED|IRQF_SHARED, QLA2XXX_DRIVER_NAME, ha);
+ IRQF_DISABLED|IRQF_SHARED, QLA2XXX_DRIVER_NAME, rsp);
if (ret) {
qla_printk(KERN_WARNING, ha,
"Failed to reserve interrupt %d already in use.\n",
@@ -1833,10 +1998,8 @@ skip_msi:
goto fail;
}
ha->flags.inta_enabled = 1;
- ha->host->irq = ha->pdev->irq;
clear_risc_ints:
- ha->isp_ops->disable_intrs(ha);
spin_lock_irq(&ha->hardware_lock);
if (IS_FWI2_CAPABLE(ha)) {
WRT_REG_DWORD(&reg->isp24.hccr, HCCRX_CLR_HOST_INT);
@@ -1853,13 +2016,74 @@ fail:
}
void
-qla2x00_free_irqs(scsi_qla_host_t *ha)
+qla2x00_free_irqs(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
+ struct rsp_que *rsp = ha->rsp_q_map[0];
if (ha->flags.msix_enabled)
qla24xx_disable_msix(ha);
else if (ha->flags.inta_enabled) {
- free_irq(ha->host->irq, ha);
+ free_irq(ha->pdev->irq, rsp);
pci_disable_msi(ha->pdev);
}
}
+
+static struct scsi_qla_host *
+qla2x00_get_rsp_host(struct rsp_que *rsp)
+{
+ srb_t *sp;
+ struct qla_hw_data *ha = rsp->hw;
+ struct scsi_qla_host *vha = NULL;
+ struct sts_entry_24xx *pkt;
+ struct req_que *req;
+
+ if (rsp->id) {
+ pkt = (struct sts_entry_24xx *) rsp->ring_ptr;
+ req = rsp->req;
+ if (pkt && pkt->handle < MAX_OUTSTANDING_COMMANDS) {
+ sp = req->outstanding_cmds[pkt->handle];
+ if (sp)
+ vha = sp->vha;
+ }
+ }
+ if (!vha)
+ /* handle it in base queue */
+ vha = pci_get_drvdata(ha->pdev);
+
+ return vha;
+}
+
+int qla25xx_request_irq(struct rsp_que *rsp)
+{
+ struct qla_hw_data *ha = rsp->hw;
+ struct qla_init_msix_entry *intr = &multi_rsp_queue;
+ struct qla_msix_entry *msix = rsp->msix;
+ int ret;
+
+ ret = request_irq(msix->vector, intr->handler, 0, intr->name, rsp);
+ if (ret) {
+ qla_printk(KERN_WARNING, ha,
+ "MSI-X: Unable to register handler -- %x/%d.\n",
+ msix->vector, ret);
+ return ret;
+ }
+ msix->have_irq = 1;
+ msix->rsp = rsp;
+ return ret;
+}
+
+void
+qla25xx_wrt_rsp_reg(struct qla_hw_data *ha, uint16_t id, uint16_t index)
+{
+ device_reg_t __iomem *reg = (void *) ha->mqiobase + QLA_QUE_PAGE * id;
+ WRT_REG_DWORD(&reg->isp25mq.rsp_q_out, index);
+}
+
+void
+qla24xx_wrt_rsp_reg(struct qla_hw_data *ha, uint16_t id, uint16_t index)
+{
+ device_reg_t __iomem *reg = (void *) ha->iobase;
+ WRT_REG_DWORD(&reg->isp24.rsp_q_out, index);
+}
+
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 3402746ec128..a99976f5fabd 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -29,7 +29,7 @@
* Kernel context.
*/
static int
-qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
+qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
{
int rval;
unsigned long flags = 0;
@@ -42,15 +42,16 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
uint32_t cnt;
uint32_t mboxes;
unsigned long wait_time;
- scsi_qla_host_t *ha = to_qla_parent(pvha);
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
reg = ha->iobase;
- io_lock_on = ha->flags.init_done;
+ io_lock_on = base_vha->flags.init_done;
rval = QLA_SUCCESS;
- abort_active = test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+ abort_active = test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
- DEBUG11(printk("%s(%ld): entered.\n", __func__, pvha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, base_vha->host_no));
/*
* Wait for active mailbox commands to finish by waiting at most tov
@@ -62,7 +63,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
mcp->tov * HZ)) {
/* Timeout occurred. Return error. */
DEBUG2_3_11(printk("%s(%ld): cmd access timeout. "
- "Exiting.\n", __func__, ha->host_no));
+ "Exiting.\n", __func__, base_vha->host_no));
return QLA_FUNCTION_TIMEOUT;
}
}
@@ -72,7 +73,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
ha->mcp = mcp;
DEBUG11(printk("scsi(%ld): prepare to issue mbox cmd=0x%x.\n",
- ha->host_no, mcp->mb[0]));
+ base_vha->host_no, mcp->mb[0]));
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -100,15 +101,16 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
#if defined(QL_DEBUG_LEVEL_1)
printk("%s(%ld): Loaded MBX registers (displayed in bytes) = \n",
- __func__, ha->host_no);
+ __func__, base_vha->host_no);
qla2x00_dump_buffer((uint8_t *)mcp->mb, 16);
printk("\n");
qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x10), 16);
printk("\n");
qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x20), 8);
printk("\n");
- printk("%s(%ld): I/O address = %p.\n", __func__, ha->host_no, optr);
- qla2x00_dump_regs(ha);
+ printk("%s(%ld): I/O address = %p.\n", __func__, base_vha->host_no,
+ optr);
+ qla2x00_dump_regs(base_vha);
#endif
/* Issue set host interrupt command to send cmd out. */
@@ -117,7 +119,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
/* Unlock mbx registers and wait for interrupt */
DEBUG11(printk("%s(%ld): going to unlock irq & waiting for interrupt. "
- "jiffies=%lx.\n", __func__, ha->host_no, jiffies));
+ "jiffies=%lx.\n", __func__, base_vha->host_no, jiffies));
/* Wait for mbx cmd completion until timeout */
@@ -137,7 +139,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
} else {
DEBUG3_11(printk("%s(%ld): cmd=%x POLLING MODE.\n", __func__,
- ha->host_no, command));
+ base_vha->host_no, command));
if (IS_FWI2_CAPABLE(ha))
WRT_REG_DWORD(&reg->isp24.hccr, HCCRX_SET_HOST_INT);
@@ -151,7 +153,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
break;
/* Check for pending interrupts. */
- qla2x00_poll(ha);
+ qla2x00_poll(ha->rsp_q_map[0]);
if (command != MBC_LOAD_RISC_RAM_EXTENDED &&
!ha->flags.mbox_int)
@@ -164,7 +166,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
uint16_t *iptr2;
DEBUG3_11(printk("%s(%ld): cmd %x completed.\n", __func__,
- ha->host_no, command));
+ base_vha->host_no, command));
/* Got interrupt. Clear the flag. */
ha->flags.mbox_int = 0;
@@ -200,12 +202,12 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
ictrl = RD_REG_WORD(&reg->isp.ictrl);
}
printk("%s(%ld): **** MB Command Timeout for cmd %x ****\n",
- __func__, ha->host_no, command);
+ __func__, base_vha->host_no, command);
printk("%s(%ld): icontrol=%x jiffies=%lx\n", __func__,
- ha->host_no, ictrl, jiffies);
+ base_vha->host_no, ictrl, jiffies);
printk("%s(%ld): *** mailbox[0] = 0x%x ***\n", __func__,
- ha->host_no, mb0);
- qla2x00_dump_regs(ha);
+ base_vha->host_no, mb0);
+ qla2x00_dump_regs(base_vha);
#endif
rval = QLA_FUNCTION_TIMEOUT;
@@ -218,10 +220,10 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
if (abort_active || !io_lock_on) {
DEBUG11(printk("%s(%ld): checking for additional resp "
- "interrupt.\n", __func__, ha->host_no));
+ "interrupt.\n", __func__, base_vha->host_no));
/* polling mode for non isp_abort commands. */
- qla2x00_poll(ha);
+ qla2x00_poll(ha->rsp_q_map[0]);
}
if (rval == QLA_FUNCTION_TIMEOUT &&
@@ -229,35 +231,37 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
if (!io_lock_on || (mcp->flags & IOCTL_CMD)) {
/* not in dpc. schedule it for dpc to take over. */
DEBUG(printk("%s(%ld): timeout schedule "
- "isp_abort_needed.\n", __func__, ha->host_no));
+ "isp_abort_needed.\n", __func__,
+ base_vha->host_no));
DEBUG2_3_11(printk("%s(%ld): timeout schedule "
- "isp_abort_needed.\n", __func__, ha->host_no));
+ "isp_abort_needed.\n", __func__,
+ base_vha->host_no));
qla_printk(KERN_WARNING, ha,
"Mailbox command timeout occurred. Scheduling ISP "
"abort.\n");
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
- qla2xxx_wake_dpc(ha);
+ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
} else if (!abort_active) {
/* call abort directly since we are in the DPC thread */
DEBUG(printk("%s(%ld): timeout calling abort_isp\n",
- __func__, ha->host_no));
+ __func__, base_vha->host_no));
DEBUG2_3_11(printk("%s(%ld): timeout calling "
- "abort_isp\n", __func__, ha->host_no));
+ "abort_isp\n", __func__, base_vha->host_no));
qla_printk(KERN_WARNING, ha,
"Mailbox command timeout occurred. Issuing ISP "
"abort.\n");
- set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
- clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
- if (qla2x00_abort_isp(ha)) {
+ set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ clear_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
+ if (qla2x00_abort_isp(base_vha)) {
/* Failed. retry later. */
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
}
- clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
DEBUG(printk("%s(%ld): finished abort_isp\n", __func__,
- ha->host_no));
+ base_vha->host_no));
DEBUG2_3_11(printk("%s(%ld): finished abort_isp\n",
- __func__, ha->host_no));
+ __func__, base_vha->host_no));
}
}
@@ -267,24 +271,26 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
if (rval) {
DEBUG2_3_11(printk("%s(%ld): **** FAILED. mbx0=%x, mbx1=%x, "
- "mbx2=%x, cmd=%x ****\n", __func__, ha->host_no,
+ "mbx2=%x, cmd=%x ****\n", __func__, base_vha->host_no,
mcp->mb[0], mcp->mb[1], mcp->mb[2], command));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__,
+ base_vha->host_no));
}
return rval;
}
int
-qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t risc_addr,
+qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr,
uint32_t risc_code_size)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
if (MSW(risc_addr) || IS_FWI2_CAPABLE(ha)) {
mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED;
@@ -312,13 +318,13 @@ qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t risc_addr,
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
- ha->host_no, rval, mcp->mb[0]));
+ vha->host_no, rval, mcp->mb[0]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -340,13 +346,14 @@ qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t risc_addr,
* Kernel context.
*/
int
-qla2x00_execute_fw(scsi_qla_host_t *ha, uint32_t risc_addr)
+qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_EXECUTE_FIRMWARE;
mcp->out_mb = MBX_0;
@@ -369,18 +376,18 @@ qla2x00_execute_fw(scsi_qla_host_t *ha, uint32_t risc_addr)
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
- ha->host_no, rval, mcp->mb[0]));
+ vha->host_no, rval, mcp->mb[0]));
} else {
if (IS_FWI2_CAPABLE(ha)) {
DEBUG11(printk("%s(%ld): done exchanges=%x.\n",
- __func__, ha->host_no, mcp->mb[1]));
+ __func__, vha->host_no, mcp->mb[1]));
} else {
DEBUG11(printk("%s(%ld): done.\n", __func__,
- ha->host_no));
+ vha->host_no));
}
}
@@ -404,28 +411,28 @@ qla2x00_execute_fw(scsi_qla_host_t *ha, uint32_t risc_addr)
* Kernel context.
*/
void
-qla2x00_get_fw_version(scsi_qla_host_t *ha, uint16_t *major, uint16_t *minor,
+qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor,
uint16_t *subminor, uint16_t *attributes, uint32_t *memory)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_GET_FIRMWARE_VERSION;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->flags = 0;
mcp->tov = MBX_TOV_SECONDS;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
/* Return mailbox data. */
*major = mcp->mb[1];
*minor = mcp->mb[2];
*subminor = mcp->mb[3];
*attributes = mcp->mb[6];
- if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ if (IS_QLA2100(vha->hw) || IS_QLA2200(vha->hw))
*memory = 0x1FFFF; /* Defaults to 128KB. */
else
*memory = (mcp->mb[5] << 16) | mcp->mb[4];
@@ -433,10 +440,10 @@ qla2x00_get_fw_version(scsi_qla_host_t *ha, uint16_t *major, uint16_t *minor,
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
/*EMPTY*/
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
}
@@ -455,32 +462,32 @@ qla2x00_get_fw_version(scsi_qla_host_t *ha, uint16_t *major, uint16_t *minor,
* Kernel context.
*/
int
-qla2x00_get_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
+qla2x00_get_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_GET_FIRMWARE_OPTION;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
fwopts[0] = mcp->mb[0];
fwopts[1] = mcp->mb[1];
fwopts[2] = mcp->mb[2];
fwopts[3] = mcp->mb[3];
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -502,13 +509,13 @@ qla2x00_get_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
* Kernel context.
*/
int
-qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
+qla2x00_set_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_SET_FIRMWARE_OPTION;
mcp->mb[1] = fwopts[1];
@@ -516,7 +523,7 @@ qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
mcp->mb[3] = fwopts[3];
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
- if (IS_FWI2_CAPABLE(ha)) {
+ if (IS_FWI2_CAPABLE(vha->hw)) {
mcp->in_mb |= MBX_1;
} else {
mcp->mb[10] = fwopts[10];
@@ -526,17 +533,17 @@ qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
}
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
fwopts[0] = mcp->mb[0];
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x (%x/%x).\n", __func__,
- ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
/*EMPTY*/
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -558,13 +565,14 @@ qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
* Kernel context.
*/
int
-qla2x00_mbx_reg_test(scsi_qla_host_t *ha)
+qla2x00_mbx_reg_test(scsi_qla_host_t *vha)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("qla2x00_mbx_reg_test(%ld): entered.\n", ha->host_no));
+ DEBUG11(printk("qla2x00_mbx_reg_test(%ld): entered.\n", vha->host_no));
mcp->mb[0] = MBC_MAILBOX_REGISTER_TEST;
mcp->mb[1] = 0xAAAA;
@@ -578,7 +586,7 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *ha)
mcp->in_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval == QLA_SUCCESS) {
if (mcp->mb[1] != 0xAAAA || mcp->mb[2] != 0x5555 ||
@@ -591,7 +599,7 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *ha)
struct device_reg_24xx __iomem *reg =
&ha->iobase->isp24;
- qla2xxx_hw_event_log(ha, HW_EVENT_ISP_ERR, 0,
+ qla2xxx_hw_event_log(vha, HW_EVENT_ISP_ERR, 0,
LSW(RD_REG_DWORD(&reg->hccr)),
LSW(RD_REG_DWORD(&reg->istatus)));
}
@@ -600,11 +608,11 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *ha)
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_mbx_reg_test(%ld): failed=%x.\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_mbx_reg_test(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -626,18 +634,18 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *ha)
* Kernel context.
*/
int
-qla2x00_verify_checksum(scsi_qla_host_t *ha, uint32_t risc_addr)
+qla2x00_verify_checksum(scsi_qla_host_t *vha, uint32_t risc_addr)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_VERIFY_CHECKSUM;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_0;
- if (IS_FWI2_CAPABLE(ha)) {
+ if (IS_FWI2_CAPABLE(vha->hw)) {
mcp->mb[1] = MSW(risc_addr);
mcp->mb[2] = LSW(risc_addr);
mcp->out_mb |= MBX_2|MBX_1;
@@ -650,14 +658,14 @@ qla2x00_verify_checksum(scsi_qla_host_t *ha, uint32_t risc_addr)
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x chk sum=%x.\n", __func__,
- ha->host_no, rval, IS_FWI2_CAPABLE(ha) ?
+ vha->host_no, rval, IS_FWI2_CAPABLE(vha->hw) ?
(mcp->mb[2] << 16) | mcp->mb[1]: mcp->mb[1]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -682,7 +690,7 @@ qla2x00_verify_checksum(scsi_qla_host_t *ha, uint32_t risc_addr)
* Kernel context.
*/
static int
-qla2x00_issue_iocb_timeout(scsi_qla_host_t *ha, void *buffer,
+qla2x00_issue_iocb_timeout(scsi_qla_host_t *vha, void *buffer,
dma_addr_t phys_addr, size_t size, uint32_t tov)
{
int rval;
@@ -699,30 +707,28 @@ qla2x00_issue_iocb_timeout(scsi_qla_host_t *ha, void *buffer,
mcp->in_mb = MBX_2|MBX_0;
mcp->tov = tov;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG(printk("qla2x00_issue_iocb(%ld): failed rval 0x%x\n",
- ha->host_no, rval));
- DEBUG2(printk("qla2x00_issue_iocb(%ld): failed rval 0x%x\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
sts_entry_t *sts_entry = (sts_entry_t *) buffer;
/* Mask reserved bits. */
sts_entry->entry_status &=
- IS_FWI2_CAPABLE(ha) ? RF_MASK_24XX :RF_MASK;
+ IS_FWI2_CAPABLE(vha->hw) ? RF_MASK_24XX : RF_MASK;
}
return rval;
}
int
-qla2x00_issue_iocb(scsi_qla_host_t *ha, void *buffer, dma_addr_t phys_addr,
+qla2x00_issue_iocb(scsi_qla_host_t *vha, void *buffer, dma_addr_t phys_addr,
size_t size)
{
- return qla2x00_issue_iocb_timeout(ha, buffer, phys_addr, size,
+ return qla2x00_issue_iocb_timeout(vha, buffer, phys_addr, size,
MBX_TOV_SECONDS);
}
@@ -741,22 +747,23 @@ qla2x00_issue_iocb(scsi_qla_host_t *ha, void *buffer, dma_addr_t phys_addr,
* Kernel context.
*/
int
-qla2x00_abort_command(scsi_qla_host_t *ha, srb_t *sp)
+qla2x00_abort_command(scsi_qla_host_t *vha, srb_t *sp, struct req_que *req)
{
unsigned long flags = 0;
fc_port_t *fcport;
int rval;
- uint32_t handle;
+ uint32_t handle = 0;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("qla2x00_abort_command(%ld): entered.\n", ha->host_no));
+ DEBUG11(printk("qla2x00_abort_command(%ld): entered.\n", vha->host_no));
fcport = sp->fcport;
spin_lock_irqsave(&ha->hardware_lock, flags);
for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
- if (ha->outstanding_cmds[handle] == sp)
+ if (req->outstanding_cmds[handle] == sp)
break;
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -778,14 +785,14 @@ qla2x00_abort_command(scsi_qla_host_t *ha, srb_t *sp)
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("qla2x00_abort_command(%ld): failed=%x.\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
DEBUG11(printk("qla2x00_abort_command(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -797,40 +804,45 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l)
int rval, rval2;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
+ struct req_que *req;
+ struct rsp_que *rsp;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no));
l = l;
- ha = fcport->ha;
+ vha = fcport->vha;
+ req = vha->hw->req_q_map[0];
+ rsp = vha->hw->rsp_q_map[0];
mcp->mb[0] = MBC_ABORT_TARGET;
mcp->out_mb = MBX_9|MBX_2|MBX_1|MBX_0;
- if (HAS_EXTENDED_IDS(ha)) {
+ if (HAS_EXTENDED_IDS(vha->hw)) {
mcp->mb[1] = fcport->loop_id;
mcp->mb[10] = 0;
mcp->out_mb |= MBX_10;
} else {
mcp->mb[1] = fcport->loop_id << 8;
}
- mcp->mb[2] = ha->loop_reset_delay;
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[2] = vha->hw->loop_reset_delay;
+ mcp->mb[9] = vha->vp_idx;
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
}
/* Issue marker IOCB. */
- rval2 = qla2x00_marker(ha, fcport->loop_id, 0, MK_SYNC_ID);
+ rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, 0,
+ MK_SYNC_ID);
if (rval2 != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB "
- "(%x).\n", __func__, ha->host_no, rval2));
+ "(%x).\n", __func__, vha->host_no, rval2));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -842,37 +854,42 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l)
int rval, rval2;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
+ struct req_que *req;
+ struct rsp_que *rsp;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no));
- ha = fcport->ha;
+ vha = fcport->vha;
+ req = vha->hw->req_q_map[0];
+ rsp = vha->hw->rsp_q_map[0];
mcp->mb[0] = MBC_LUN_RESET;
mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0;
- if (HAS_EXTENDED_IDS(ha))
+ if (HAS_EXTENDED_IDS(vha->hw))
mcp->mb[1] = fcport->loop_id;
else
mcp->mb[1] = fcport->loop_id << 8;
mcp->mb[2] = l;
mcp->mb[3] = 0;
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[9] = vha->vp_idx;
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
}
/* Issue marker IOCB. */
- rval2 = qla2x00_marker(ha, fcport->loop_id, l, MK_SYNC_ID_LUN);
+ rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, l,
+ MK_SYNC_ID_LUN);
if (rval2 != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB "
- "(%x).\n", __func__, ha->host_no, rval2));
+ "(%x).\n", __func__, vha->host_no, rval2));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -899,7 +916,7 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l)
* Kernel context.
*/
int
-qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
+qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
uint8_t *area, uint8_t *domain, uint16_t *top, uint16_t *sw_cap)
{
int rval;
@@ -907,15 +924,15 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_adapter_id(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
mcp->mb[0] = MBC_GET_ADAPTER_LOOP_ID;
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[9] = vha->vp_idx;
mcp->out_mb = MBX_9|MBX_0;
mcp->in_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (mcp->mb[0] == MBS_COMMAND_ERROR)
rval = QLA_COMMAND_ERROR;
else if (mcp->mb[0] == MBS_INVALID_COMMAND)
@@ -932,11 +949,11 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_adapter_id(%ld): failed=%x.\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_get_adapter_id(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -958,7 +975,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
* Kernel context.
*/
int
-qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov,
+qla2x00_get_retry_cnt(scsi_qla_host_t *vha, uint8_t *retry_cnt, uint8_t *tov,
uint16_t *r_a_tov)
{
int rval;
@@ -967,19 +984,19 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov,
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_retry_cnt(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
mcp->mb[0] = MBC_GET_RETRY_COUNT;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_retry_cnt(%ld): failed = %x.\n",
- ha->host_no, mcp->mb[0]));
+ vha->host_no, mcp->mb[0]));
} else {
/* Convert returned data and check our values. */
*r_a_tov = mcp->mb[3] / 2;
@@ -991,7 +1008,7 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov,
}
DEBUG11(printk("qla2x00_get_retry_cnt(%ld): done. mb3=%d "
- "ratov=%d.\n", ha->host_no, mcp->mb[3], ratov));
+ "ratov=%d.\n", vha->host_no, mcp->mb[3], ratov));
}
return rval;
@@ -1015,14 +1032,15 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov,
* Kernel context.
*/
int
-qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size)
+qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
if (ha->flags.npiv_supported)
mcp->mb[0] = MBC_MID_INITIALIZE_FIRMWARE;
@@ -1040,17 +1058,17 @@ qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size)
mcp->buf_size = size;
mcp->flags = MBX_DMA_OUT;
mcp->tov = MBX_TOV_SECONDS;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_init_firmware(%ld): failed=%x "
"mb0=%x.\n",
- ha->host_no, rval, mcp->mb[0]));
+ vha->host_no, rval, mcp->mb[0]));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_init_firmware(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1073,7 +1091,7 @@ qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size)
* Kernel context.
*/
int
-qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
+qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
{
int rval;
mbx_cmd_t mc;
@@ -1081,14 +1099,15 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
port_database_t *pd;
struct port_database_24xx *pd24;
dma_addr_t pd_dma;
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
pd24 = NULL;
pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma);
if (pd == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Port Database "
- "structure.\n", __func__, ha->host_no));
+ "structure.\n", __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(pd, 0, max(PORT_DATABASE_SIZE, PORT_DATABASE_24XX_SIZE));
@@ -1100,7 +1119,7 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
mcp->mb[3] = LSW(pd_dma);
mcp->mb[6] = MSW(MSD(pd_dma));
mcp->mb[7] = LSW(MSD(pd_dma));
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[9] = vha->vp_idx;
mcp->out_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
mcp->in_mb = MBX_0;
if (IS_FWI2_CAPABLE(ha)) {
@@ -1120,7 +1139,7 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
PORT_DATABASE_24XX_SIZE : PORT_DATABASE_SIZE;
mcp->flags = MBX_DMA_IN;
mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS)
goto gpd_error_out;
@@ -1132,7 +1151,7 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
pd24->last_login_state != PDS_PRLI_COMPLETE) {
DEBUG2(printk("%s(%ld): Unable to verify "
"login-state (%x/%x) for loop_id %x\n",
- __func__, ha->host_no,
+ __func__, vha->host_no,
pd24->current_login_state,
pd24->last_login_state, fcport->loop_id));
rval = QLA_FUNCTION_FAILED;
@@ -1192,9 +1211,9 @@ gpd_error_out:
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
- __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -1217,21 +1236,21 @@ gpd_error_out:
* Kernel context.
*/
int
-qla2x00_get_firmware_state(scsi_qla_host_t *ha, uint16_t *states)
+qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_firmware_state(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
/* Return firmware states. */
states[0] = mcp->mb[1];
@@ -1241,11 +1260,11 @@ qla2x00_get_firmware_state(scsi_qla_host_t *ha, uint16_t *states)
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_firmware_state(%ld): "
- "failed=%x.\n", ha->host_no, rval));
+ "failed=%x.\n", vha->host_no, rval));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_get_firmware_state(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1270,7 +1289,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *ha, uint16_t *states)
* Kernel context.
*/
int
-qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
+qla2x00_get_port_name(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t *name,
uint8_t opt)
{
int rval;
@@ -1278,12 +1297,12 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_port_name(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
mcp->mb[0] = MBC_GET_PORT_NAME;
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[9] = vha->vp_idx;
mcp->out_mb = MBX_9|MBX_1|MBX_0;
- if (HAS_EXTENDED_IDS(ha)) {
+ if (HAS_EXTENDED_IDS(vha->hw)) {
mcp->mb[1] = loop_id;
mcp->mb[10] = opt;
mcp->out_mb |= MBX_10;
@@ -1294,12 +1313,12 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
mcp->in_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_port_name(%ld): failed=%x.\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
if (name != NULL) {
/* This function returns name in big endian. */
@@ -1314,7 +1333,7 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
}
DEBUG11(printk("qla2x00_get_port_name(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1336,45 +1355,45 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
* Kernel context.
*/
int
-qla2x00_lip_reset(scsi_qla_host_t *ha)
+qla2x00_lip_reset(scsi_qla_host_t *vha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
- if (IS_FWI2_CAPABLE(ha)) {
+ if (IS_FWI2_CAPABLE(vha->hw)) {
mcp->mb[0] = MBC_LIP_FULL_LOGIN;
mcp->mb[1] = BIT_6;
mcp->mb[2] = 0;
- mcp->mb[3] = ha->loop_reset_delay;
+ mcp->mb[3] = vha->hw->loop_reset_delay;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
} else {
mcp->mb[0] = MBC_LIP_RESET;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
- if (HAS_EXTENDED_IDS(ha)) {
+ if (HAS_EXTENDED_IDS(vha->hw)) {
mcp->mb[1] = 0x00ff;
mcp->mb[10] = 0;
mcp->out_mb |= MBX_10;
} else {
mcp->mb[1] = 0xff00;
}
- mcp->mb[2] = ha->loop_reset_delay;
+ mcp->mb[2] = vha->hw->loop_reset_delay;
mcp->mb[3] = 0;
}
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n",
- __func__, ha->host_no, rval));
+ __func__, vha->host_no, rval));
} else {
/*EMPTY*/
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -1399,7 +1418,7 @@ qla2x00_lip_reset(scsi_qla_host_t *ha)
* Kernel context.
*/
int
-qla2x00_send_sns(scsi_qla_host_t *ha, dma_addr_t sns_phys_address,
+qla2x00_send_sns(scsi_qla_host_t *vha, dma_addr_t sns_phys_address,
uint16_t cmd_size, size_t buf_size)
{
int rval;
@@ -1407,10 +1426,11 @@ qla2x00_send_sns(scsi_qla_host_t *ha, dma_addr_t sns_phys_address,
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_send_sns(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
DEBUG11(printk("qla2x00_send_sns: retry cnt=%d ratov=%d total "
- "tov=%d.\n", ha->retry_count, ha->login_timeout, mcp->tov));
+ "tov=%d.\n", vha->hw->retry_count, vha->hw->login_timeout,
+ mcp->tov));
mcp->mb[0] = MBC_SEND_SNS_COMMAND;
mcp->mb[1] = cmd_size;
@@ -1422,25 +1442,25 @@ qla2x00_send_sns(scsi_qla_host_t *ha, dma_addr_t sns_phys_address,
mcp->in_mb = MBX_0|MBX_1;
mcp->buf_size = buf_size;
mcp->flags = MBX_DMA_OUT|MBX_DMA_IN;
- mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
- rval = qla2x00_mailbox_command(ha, mcp);
+ mcp->tov = (vha->hw->login_timeout * 2) + (vha->hw->login_timeout / 2);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x "
- "mb[1]=%x.\n", ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ "mb[1]=%x.\n", vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
DEBUG2_3_11(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x "
- "mb[1]=%x.\n", ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ "mb[1]=%x.\n", vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
/*EMPTY*/
- DEBUG11(printk("qla2x00_send_sns(%ld): done.\n", ha->host_no));
+ DEBUG11(printk("qla2x00_send_sns(%ld): done.\n", vha->host_no));
}
return rval;
}
int
-qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
+qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt)
{
int rval;
@@ -1448,13 +1468,14 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
struct logio_entry_24xx *lg;
dma_addr_t lg_dma;
uint32_t iop[2];
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
if (lg == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Login IOCB.\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(lg, 0, sizeof(struct logio_entry_24xx));
@@ -1470,14 +1491,14 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
lg->port_id[0] = al_pa;
lg->port_id[1] = area;
lg->port_id[2] = domain;
- lg->vp_index = ha->vp_idx;
- rval = qla2x00_issue_iocb(ha, lg, lg_dma, 0);
+ lg->vp_index = vha->vp_idx;
+ rval = qla2x00_issue_iocb(vha, lg, lg_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue Login IOCB "
- "(%x).\n", __func__, ha->host_no, rval));
+ "(%x).\n", __func__, vha->host_no, rval));
} else if (lg->entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, ha->host_no,
+ "-- error status (%x).\n", __func__, vha->host_no,
lg->entry_status));
rval = QLA_FUNCTION_FAILED;
} else if (lg->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
@@ -1486,7 +1507,7 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
"-- completion status (%x) ioparam=%x/%x.\n", __func__,
- ha->host_no, le16_to_cpu(lg->comp_status), iop[0],
+ vha->host_no, le16_to_cpu(lg->comp_status), iop[0],
iop[1]));
switch (iop[0]) {
@@ -1515,7 +1536,7 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
break;
}
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
iop[0] = le32_to_cpu(lg->io_parameter[0]);
@@ -1562,14 +1583,15 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
* Kernel context.
*/
int
-qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
+qla2x00_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("qla2x00_login_fabric(%ld): entered.\n", ha->host_no));
+ DEBUG11(printk("qla2x00_login_fabric(%ld): entered.\n", vha->host_no));
mcp->mb[0] = MBC_LOGIN_FABRIC_PORT;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
@@ -1586,7 +1608,7 @@ qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
mcp->in_mb = MBX_7|MBX_6|MBX_2|MBX_1|MBX_0;
mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
/* Return mailbox statuses. */
if (mb != NULL) {
@@ -1613,12 +1635,12 @@ qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_login_fabric(%ld): failed=%x "
- "mb[0]=%x mb[1]=%x mb[2]=%x.\n", ha->host_no, rval,
+ "mb[0]=%x mb[1]=%x mb[2]=%x.\n", vha->host_no, rval,
mcp->mb[0], mcp->mb[1], mcp->mb[2]));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_login_fabric(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1641,19 +1663,20 @@ qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
*
*/
int
-qla2x00_login_local_device(scsi_qla_host_t *ha, fc_port_t *fcport,
+qla2x00_login_local_device(scsi_qla_host_t *vha, fc_port_t *fcport,
uint16_t *mb_ret, uint8_t opt)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
if (IS_FWI2_CAPABLE(ha))
- return qla24xx_login_fabric(ha, fcport->loop_id,
+ return qla24xx_login_fabric(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa, mb_ret, opt);
- DEBUG3(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG3(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_LOGIN_LOOP_PORT;
if (HAS_EXTENDED_IDS(ha))
@@ -1665,7 +1688,7 @@ qla2x00_login_local_device(scsi_qla_host_t *ha, fc_port_t *fcport,
mcp->in_mb = MBX_7|MBX_6|MBX_1|MBX_0;
mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
/* Return mailbox statuses. */
if (mb_ret != NULL) {
@@ -1686,33 +1709,34 @@ qla2x00_login_local_device(scsi_qla_host_t *ha, fc_port_t *fcport,
rval = QLA_SUCCESS;
DEBUG(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x "
- "mb[6]=%x mb[7]=%x.\n", __func__, ha->host_no, rval,
+ "mb[6]=%x mb[7]=%x.\n", __func__, vha->host_no, rval,
mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]));
DEBUG2_3(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x "
- "mb[6]=%x mb[7]=%x.\n", __func__, ha->host_no, rval,
+ "mb[6]=%x mb[7]=%x.\n", __func__, vha->host_no, rval,
mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]));
} else {
/*EMPTY*/
- DEBUG3(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG3(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return (rval);
}
int
-qla24xx_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
+qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
uint8_t area, uint8_t al_pa)
{
int rval;
struct logio_entry_24xx *lg;
dma_addr_t lg_dma;
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
if (lg == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Logout IOCB.\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(lg, 0, sizeof(struct logio_entry_24xx));
@@ -1725,25 +1749,26 @@ qla24xx_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
lg->port_id[0] = al_pa;
lg->port_id[1] = area;
lg->port_id[2] = domain;
- lg->vp_index = ha->vp_idx;
- rval = qla2x00_issue_iocb(ha, lg, lg_dma, 0);
+ lg->vp_index = vha->vp_idx;
+
+ rval = qla2x00_issue_iocb(vha, lg, lg_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue Logout IOCB "
- "(%x).\n", __func__, ha->host_no, rval));
+ "(%x).\n", __func__, vha->host_no, rval));
} else if (lg->entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, ha->host_no,
+ "-- error status (%x).\n", __func__, vha->host_no,
lg->entry_status));
rval = QLA_FUNCTION_FAILED;
} else if (lg->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
- DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+ DEBUG2_3_11(printk("%s(%ld %d): failed to complete IOCB "
"-- completion status (%x) ioparam=%x/%x.\n", __func__,
- ha->host_no, le16_to_cpu(lg->comp_status),
+ vha->host_no, vha->vp_idx, le16_to_cpu(lg->comp_status),
le32_to_cpu(lg->io_parameter[0]),
le32_to_cpu(lg->io_parameter[1])));
} else {
/*EMPTY*/
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
dma_pool_free(ha->s_dma_pool, lg, lg_dma);
@@ -1768,7 +1793,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
* Kernel context.
*/
int
-qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
+qla2x00_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
uint8_t area, uint8_t al_pa)
{
int rval;
@@ -1776,11 +1801,11 @@ qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_fabric_logout(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
mcp->mb[0] = MBC_LOGOUT_FABRIC_PORT;
mcp->out_mb = MBX_1|MBX_0;
- if (HAS_EXTENDED_IDS(ha)) {
+ if (HAS_EXTENDED_IDS(vha->hw)) {
mcp->mb[1] = loop_id;
mcp->mb[10] = 0;
mcp->out_mb |= MBX_10;
@@ -1791,16 +1816,16 @@ qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
mcp->in_mb = MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_fabric_logout(%ld): failed=%x "
- "mbx1=%x.\n", ha->host_no, rval, mcp->mb[1]));
+ "mbx1=%x.\n", vha->host_no, rval, mcp->mb[1]));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_fabric_logout(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1822,33 +1847,33 @@ qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
* Kernel context.
*/
int
-qla2x00_full_login_lip(scsi_qla_host_t *ha)
+qla2x00_full_login_lip(scsi_qla_host_t *vha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_full_login_lip(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
mcp->mb[0] = MBC_LIP_FULL_LOGIN;
- mcp->mb[1] = IS_FWI2_CAPABLE(ha) ? BIT_3: 0;
+ mcp->mb[1] = IS_FWI2_CAPABLE(vha->hw) ? BIT_3 : 0;
mcp->mb[2] = 0;
mcp->mb[3] = 0;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_full_login_lip(%ld): failed=%x.\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_full_login_lip(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1867,7 +1892,7 @@ qla2x00_full_login_lip(scsi_qla_host_t *ha)
* Kernel context.
*/
int
-qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma,
+qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma,
uint16_t *entries)
{
int rval;
@@ -1875,20 +1900,20 @@ qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma,
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_id_list(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
if (id_list == NULL)
return QLA_FUNCTION_FAILED;
mcp->mb[0] = MBC_GET_ID_LIST;
mcp->out_mb = MBX_0;
- if (IS_FWI2_CAPABLE(ha)) {
+ if (IS_FWI2_CAPABLE(vha->hw)) {
mcp->mb[2] = MSW(id_list_dma);
mcp->mb[3] = LSW(id_list_dma);
mcp->mb[6] = MSW(MSD(id_list_dma));
mcp->mb[7] = LSW(MSD(id_list_dma));
mcp->mb[8] = 0;
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[9] = vha->vp_idx;
mcp->out_mb |= MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2;
} else {
mcp->mb[1] = MSW(id_list_dma);
@@ -1900,16 +1925,16 @@ qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma,
mcp->in_mb = MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_id_list(%ld): failed=%x.\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
*entries = mcp->mb[1];
DEBUG11(printk("qla2x00_get_id_list(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1929,7 +1954,7 @@ qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma,
* Kernel context.
*/
int
-qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt,
+qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
uint16_t *orig_xchg_cnt, uint16_t *cur_iocb_cnt,
uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports)
{
@@ -1937,22 +1962,22 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt,
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed = %x.\n", __func__,
- ha->host_no, mcp->mb[0]));
+ vha->host_no, mcp->mb[0]));
} else {
DEBUG11(printk("%s(%ld): done. mb1=%x mb2=%x mb3=%x mb6=%x "
- "mb7=%x mb10=%x mb11=%x.\n", __func__, ha->host_no,
+ "mb7=%x mb10=%x mb11=%x.\n", __func__, vha->host_no,
mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[6], mcp->mb[7],
mcp->mb[10], mcp->mb[11]));
@@ -1964,7 +1989,7 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt,
*cur_iocb_cnt = mcp->mb[7];
if (orig_iocb_cnt)
*orig_iocb_cnt = mcp->mb[10];
- if (ha->flags.npiv_supported && max_npiv_vports)
+ if (vha->hw->flags.npiv_supported && max_npiv_vports)
*max_npiv_vports = mcp->mb[11];
}
@@ -1987,18 +2012,19 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt,
* Kernel context.
*/
int
-qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map)
+qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
char *pmap;
dma_addr_t pmap_dma;
+ struct qla_hw_data *ha = vha->hw;
pmap = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pmap_dma);
if (pmap == NULL) {
DEBUG2_3_11(printk("%s(%ld): **** Mem Alloc Failed ****",
- __func__, ha->host_no));
+ __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(pmap, 0, FCAL_MAP_SIZE);
@@ -2013,11 +2039,11 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map)
mcp->buf_size = FCAL_MAP_SIZE;
mcp->flags = MBX_DMA_IN;
mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval == QLA_SUCCESS) {
DEBUG11(printk("%s(%ld): (mb0=%x/mb1=%x) FC/AL Position Map "
- "size (%x)\n", __func__, ha->host_no, mcp->mb[0],
+ "size (%x)\n", __func__, vha->host_no, mcp->mb[0],
mcp->mb[1], (unsigned)pmap[0]));
DEBUG11(qla2x00_dump_buffer(pmap, pmap[0] + 1));
@@ -2028,9 +2054,9 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map)
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -2051,15 +2077,16 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map)
* BIT_1 = mailbox error.
*/
int
-qla2x00_get_link_status(scsi_qla_host_t *ha, uint16_t loop_id,
+qla2x00_get_link_status(scsi_qla_host_t *vha, uint16_t loop_id,
struct link_statistics *stats, dma_addr_t stats_dma)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
uint32_t *siter, *diter, dwords;
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_GET_LINK_STATUS;
mcp->mb[2] = MSW(stats_dma);
@@ -2084,12 +2111,12 @@ qla2x00_get_link_status(scsi_qla_host_t *ha, uint16_t loop_id,
}
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = IOCTL_CMD;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval == QLA_SUCCESS) {
if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
DEBUG2_3_11(printk("%s(%ld): cmd failed. mbx0=%x.\n",
- __func__, ha->host_no, mcp->mb[0]));
+ __func__, vha->host_no, mcp->mb[0]));
rval = QLA_FUNCTION_FAILED;
} else {
/* Copy over data -- firmware data is LE. */
@@ -2101,14 +2128,14 @@ qla2x00_get_link_status(scsi_qla_host_t *ha, uint16_t loop_id,
} else {
/* Failed. */
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
}
return rval;
}
int
-qla24xx_get_isp_stats(scsi_qla_host_t *ha, struct link_statistics *stats,
+qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats,
dma_addr_t stats_dma)
{
int rval;
@@ -2116,7 +2143,7 @@ qla24xx_get_isp_stats(scsi_qla_host_t *ha, struct link_statistics *stats,
mbx_cmd_t *mcp = &mc;
uint32_t *siter, *diter, dwords;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_GET_LINK_PRIV_STATS;
mcp->mb[2] = MSW(stats_dma);
@@ -2124,18 +2151,18 @@ qla24xx_get_isp_stats(scsi_qla_host_t *ha, struct link_statistics *stats,
mcp->mb[6] = MSW(MSD(stats_dma));
mcp->mb[7] = LSW(MSD(stats_dma));
mcp->mb[8] = sizeof(struct link_statistics) / 4;
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[9] = vha->vp_idx;
mcp->mb[10] = 0;
mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
mcp->in_mb = MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = IOCTL_CMD;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval == QLA_SUCCESS) {
if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
DEBUG2_3_11(printk("%s(%ld): cmd failed. mbx0=%x.\n",
- __func__, ha->host_no, mcp->mb[0]));
+ __func__, vha->host_no, mcp->mb[0]));
rval = QLA_FUNCTION_FAILED;
} else {
/* Copy over data -- firmware data is LE. */
@@ -2147,14 +2174,14 @@ qla24xx_get_isp_stats(scsi_qla_host_t *ha, struct link_statistics *stats,
} else {
/* Failed. */
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
}
return rval;
}
int
-qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp)
+qla24xx_abort_command(scsi_qla_host_t *vha, srb_t *sp, struct req_que *req)
{
int rval;
fc_port_t *fcport;
@@ -2163,18 +2190,18 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp)
struct abort_entry_24xx *abt;
dma_addr_t abt_dma;
uint32_t handle;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
fcport = sp->fcport;
- spin_lock_irqsave(&pha->hardware_lock, flags);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
- if (pha->outstanding_cmds[handle] == sp)
+ if (req->outstanding_cmds[handle] == sp)
break;
}
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (handle == MAX_OUTSTANDING_COMMANDS) {
/* Command not found. */
return QLA_FUNCTION_FAILED;
@@ -2183,7 +2210,7 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp)
abt = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma);
if (abt == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Abort IOCB.\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(abt, 0, sizeof(struct abort_entry_24xx));
@@ -2196,22 +2223,25 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp)
abt->port_id[1] = fcport->d_id.b.area;
abt->port_id[2] = fcport->d_id.b.domain;
abt->vp_index = fcport->vp_idx;
- rval = qla2x00_issue_iocb(ha, abt, abt_dma, 0);
+
+ abt->req_que_no = cpu_to_le16(req->id);
+
+ rval = qla2x00_issue_iocb(vha, abt, abt_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue IOCB (%x).\n",
- __func__, ha->host_no, rval));
+ __func__, vha->host_no, rval));
} else if (abt->entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, ha->host_no,
+ "-- error status (%x).\n", __func__, vha->host_no,
abt->entry_status));
rval = QLA_FUNCTION_FAILED;
} else if (abt->nport_handle != __constant_cpu_to_le16(0)) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- completion status (%x).\n", __func__, ha->host_no,
+ "-- completion status (%x).\n", __func__, vha->host_no,
le16_to_cpu(abt->nport_handle)));
rval = QLA_FUNCTION_FAILED;
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
dma_pool_free(ha->s_dma_pool, abt, abt_dma);
@@ -2233,16 +2263,21 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
int rval, rval2;
struct tsk_mgmt_cmd *tsk;
dma_addr_t tsk_dma;
- scsi_qla_host_t *ha, *pha;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct req_que *req;
+ struct rsp_que *rsp;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no));
- ha = fcport->ha;
- pha = to_qla_parent(ha);
- tsk = dma_pool_alloc(pha->s_dma_pool, GFP_KERNEL, &tsk_dma);
+ vha = fcport->vha;
+ ha = vha->hw;
+ req = ha->req_q_map[0];
+ rsp = ha->rsp_q_map[0];
+ tsk = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &tsk_dma);
if (tsk == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Task Management "
- "IOCB.\n", __func__, ha->host_no));
+ "IOCB.\n", __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(tsk, 0, sizeof(struct tsk_mgmt_cmd));
@@ -2262,34 +2297,34 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
sizeof(tsk->p.tsk.lun));
}
- rval = qla2x00_issue_iocb(ha, tsk, tsk_dma, 0);
+ rval = qla2x00_issue_iocb(vha, tsk, tsk_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue %s Reset IOCB "
- "(%x).\n", __func__, ha->host_no, name, rval));
+ "(%x).\n", __func__, vha->host_no, name, rval));
} else if (tsk->p.sts.entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, ha->host_no,
+ "-- error status (%x).\n", __func__, vha->host_no,
tsk->p.sts.entry_status));
rval = QLA_FUNCTION_FAILED;
} else if (tsk->p.sts.comp_status !=
__constant_cpu_to_le16(CS_COMPLETE)) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
"-- completion status (%x).\n", __func__,
- ha->host_no, le16_to_cpu(tsk->p.sts.comp_status)));
+ vha->host_no, le16_to_cpu(tsk->p.sts.comp_status)));
rval = QLA_FUNCTION_FAILED;
}
/* Issue marker IOCB. */
- rval2 = qla2x00_marker(ha, fcport->loop_id, l,
+ rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, l,
type == TCF_LUN_RESET ? MK_SYNC_ID_LUN: MK_SYNC_ID);
if (rval2 != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB "
- "(%x).\n", __func__, ha->host_no, rval2));
+ "(%x).\n", __func__, vha->host_no, rval2));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
- dma_pool_free(pha->s_dma_pool, tsk, tsk_dma);
+ dma_pool_free(ha->s_dma_pool, tsk, tsk_dma);
return rval;
}
@@ -2307,29 +2342,30 @@ qla24xx_lun_reset(struct fc_port *fcport, unsigned int l)
}
int
-qla2x00_system_error(scsi_qla_host_t *ha)
+qla2x00_system_error(scsi_qla_host_t *vha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
if (!IS_QLA23XX(ha) && !IS_FWI2_CAPABLE(ha))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_GEN_SYSTEM_ERROR;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 5;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -2342,14 +2378,14 @@ qla2x00_system_error(scsi_qla_host_t *ha)
* Returns
*/
int
-qla2x00_set_serdes_params(scsi_qla_host_t *ha, uint16_t sw_em_1g,
+qla2x00_set_serdes_params(scsi_qla_host_t *vha, uint16_t sw_em_1g,
uint16_t sw_em_2g, uint16_t sw_em_4g)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_SERDES_PARAMS;
mcp->mb[1] = BIT_0;
@@ -2360,61 +2396,61 @@ qla2x00_set_serdes_params(scsi_qla_host_t *ha, uint16_t sw_em_1g,
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__,
- ha->host_no, rval, mcp->mb[0]));
+ vha->host_no, rval, mcp->mb[0]));
} else {
/*EMPTY*/
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
}
int
-qla2x00_stop_firmware(scsi_qla_host_t *ha)
+qla2x00_stop_firmware(scsi_qla_host_t *vha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_FWI2_CAPABLE(ha))
+ if (!IS_FWI2_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_STOP_FIRMWARE;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 5;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
}
int
-qla2x00_enable_eft_trace(scsi_qla_host_t *ha, dma_addr_t eft_dma,
+qla2x00_enable_eft_trace(scsi_qla_host_t *vha, dma_addr_t eft_dma,
uint16_t buffers)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_FWI2_CAPABLE(ha))
+ if (!IS_FWI2_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_TRACE_CONTROL;
mcp->mb[1] = TC_EFT_ENABLE;
@@ -2428,28 +2464,28 @@ qla2x00_enable_eft_trace(scsi_qla_host_t *ha, dma_addr_t eft_dma,
mcp->in_mb = MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
- __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
}
int
-qla2x00_disable_eft_trace(scsi_qla_host_t *ha)
+qla2x00_disable_eft_trace(scsi_qla_host_t *vha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_FWI2_CAPABLE(ha))
+ if (!IS_FWI2_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_TRACE_CONTROL;
mcp->mb[1] = TC_EFT_DISABLE;
@@ -2457,29 +2493,29 @@ qla2x00_disable_eft_trace(scsi_qla_host_t *ha)
mcp->in_mb = MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
- __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
}
int
-qla2x00_enable_fce_trace(scsi_qla_host_t *ha, dma_addr_t fce_dma,
+qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma,
uint16_t buffers, uint16_t *mb, uint32_t *dwords)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_QLA25XX(ha))
+ if (!IS_QLA25XX(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_TRACE_CONTROL;
mcp->mb[1] = TC_FCE_ENABLE;
@@ -2497,12 +2533,12 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *ha, dma_addr_t fce_dma,
mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
- __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
if (mb)
memcpy(mb, mcp->mb, 8 * sizeof(*mb));
@@ -2514,16 +2550,16 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *ha, dma_addr_t fce_dma,
}
int
-qla2x00_disable_fce_trace(scsi_qla_host_t *ha, uint64_t *wr, uint64_t *rd)
+qla2x00_disable_fce_trace(scsi_qla_host_t *vha, uint64_t *wr, uint64_t *rd)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_FWI2_CAPABLE(ha))
+ if (!IS_FWI2_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_TRACE_CONTROL;
mcp->mb[1] = TC_FCE_DISABLE;
@@ -2533,12 +2569,12 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *ha, uint64_t *wr, uint64_t *rd)
MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
- __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
if (wr)
*wr = (uint64_t) mcp->mb[5] << 48 |
@@ -2556,17 +2592,17 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *ha, uint64_t *wr, uint64_t *rd)
}
int
-qla2x00_read_sfp(scsi_qla_host_t *ha, dma_addr_t sfp_dma, uint16_t addr,
+qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint16_t addr,
uint16_t off, uint16_t count)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_FWI2_CAPABLE(ha))
+ if (!IS_FWI2_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_READ_SFP;
mcp->mb[1] = addr;
@@ -2581,30 +2617,30 @@ qla2x00_read_sfp(scsi_qla_host_t *ha, dma_addr_t sfp_dma, uint16_t addr,
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__,
- ha->host_no, rval, mcp->mb[0]));
+ vha->host_no, rval, mcp->mb[0]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
}
int
-qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id,
+qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
uint16_t port_speed, uint16_t *mb)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_IIDMA_CAPABLE(ha))
+ if (!IS_IIDMA_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_PORT_PARAMS;
mcp->mb[1] = loop_id;
@@ -2615,7 +2651,7 @@ qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id,
mcp->in_mb = MBX_5|MBX_4|MBX_3|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
/* Return mailbox statuses. */
if (mb != NULL) {
@@ -2628,28 +2664,29 @@ qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id,
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
}
void
-qla24xx_report_id_acquisition(scsi_qla_host_t *ha,
+qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
struct vp_rpt_id_entry_24xx *rptid_entry)
{
uint8_t vp_idx;
uint16_t stat = le16_to_cpu(rptid_entry->vp_idx);
- scsi_qla_host_t *vha;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *vp;
if (rptid_entry->entry_status != 0)
return;
if (rptid_entry->format == 0) {
DEBUG15(printk("%s:format 0 : scsi(%ld) number of VPs setup %d,"
- " number of VPs acquired %d\n", __func__, ha->host_no,
+ " number of VPs acquired %d\n", __func__, vha->host_no,
MSB(rptid_entry->vp_count), LSB(rptid_entry->vp_count)));
DEBUG15(printk("%s primary port id %02x%02x%02x\n", __func__,
rptid_entry->port_id[2], rptid_entry->port_id[1],
@@ -2658,7 +2695,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *ha,
vp_idx = LSB(stat);
DEBUG15(printk("%s:format 1: scsi(%ld): VP[%d] enabled "
"- status %d - "
- "with port id %02x%02x%02x\n",__func__,ha->host_no,
+ "with port id %02x%02x%02x\n", __func__, vha->host_no,
vp_idx, MSB(stat),
rptid_entry->port_id[2], rptid_entry->port_id[1],
rptid_entry->port_id[0]));
@@ -2668,25 +2705,24 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *ha,
if (MSB(stat) == 1)
return;
- list_for_each_entry(vha, &ha->vp_list, vp_list)
- if (vp_idx == vha->vp_idx)
+ list_for_each_entry(vp, &ha->vp_list, list)
+ if (vp_idx == vp->vp_idx)
break;
-
- if (!vha)
+ if (!vp)
return;
- vha->d_id.b.domain = rptid_entry->port_id[2];
- vha->d_id.b.area = rptid_entry->port_id[1];
- vha->d_id.b.al_pa = rptid_entry->port_id[0];
+ vp->d_id.b.domain = rptid_entry->port_id[2];
+ vp->d_id.b.area = rptid_entry->port_id[1];
+ vp->d_id.b.al_pa = rptid_entry->port_id[0];
/*
* Cannot configure here as we are still sitting on the
* response queue. Handle it in dpc context.
*/
- set_bit(VP_IDX_ACQUIRED, &vha->vp_flags);
- set_bit(VP_DPC_NEEDED, &ha->dpc_flags);
+ set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
+ set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
- qla2xxx_wake_dpc(ha);
+ qla2xxx_wake_dpc(vha);
}
}
@@ -2709,15 +2745,15 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
int rval;
struct vp_config_entry_24xx *vpmod;
dma_addr_t vpmod_dma;
- scsi_qla_host_t *pha;
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
/* This can be called by the parent */
- pha = to_qla_parent(vha);
- vpmod = dma_pool_alloc(pha->s_dma_pool, GFP_KERNEL, &vpmod_dma);
+ vpmod = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vpmod_dma);
if (!vpmod) {
DEBUG2_3(printk("%s(%ld): failed to allocate Modify VP "
- "IOCB.\n", __func__, pha->host_no));
+ "IOCB.\n", __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
@@ -2732,26 +2768,27 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
memcpy(vpmod->port_name_idx1, vha->port_name, WWN_SIZE);
vpmod->entry_count = 1;
- rval = qla2x00_issue_iocb(pha, vpmod, vpmod_dma, 0);
+ rval = qla2x00_issue_iocb(base_vha, vpmod, vpmod_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue VP config IOCB"
- "(%x).\n", __func__, pha->host_no, rval));
+ "(%x).\n", __func__, base_vha->host_no, rval));
} else if (vpmod->comp_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, pha->host_no,
+ "-- error status (%x).\n", __func__, base_vha->host_no,
vpmod->comp_status));
rval = QLA_FUNCTION_FAILED;
} else if (vpmod->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- completion status (%x).\n", __func__, pha->host_no,
+ "-- completion status (%x).\n", __func__, base_vha->host_no,
le16_to_cpu(vpmod->comp_status)));
rval = QLA_FUNCTION_FAILED;
} else {
/* EMPTY */
- DEBUG11(printk("%s(%ld): done.\n", __func__, pha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__,
+ base_vha->host_no));
fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING);
}
- dma_pool_free(pha->s_dma_pool, vpmod, vpmod_dma);
+ dma_pool_free(ha->s_dma_pool, vpmod, vpmod_dma);
return rval;
}
@@ -2778,11 +2815,12 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
int map, pos;
struct vp_ctrl_entry_24xx *vce;
dma_addr_t vce_dma;
- scsi_qla_host_t *ha = vha->parent;
+ struct qla_hw_data *ha = vha->hw;
int vp_index = vha->vp_idx;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
DEBUG11(printk("%s(%ld): entered. Enabling index %d\n", __func__,
- ha->host_no, vp_index));
+ vha->host_no, vp_index));
if (vp_index == 0 || vp_index >= ha->max_npiv_vports)
return QLA_PARAMETER_ERROR;
@@ -2791,7 +2829,7 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
if (!vce) {
DEBUG2_3(printk("%s(%ld): "
"failed to allocate VP Control IOCB.\n", __func__,
- ha->host_no));
+ base_vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(vce, 0, sizeof(struct vp_ctrl_entry_24xx));
@@ -2810,30 +2848,30 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
vce->vp_idx_map[map] |= 1 << pos;
mutex_unlock(&ha->vport_lock);
- rval = qla2x00_issue_iocb(ha, vce, vce_dma, 0);
+ rval = qla2x00_issue_iocb(base_vha, vce, vce_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue VP control IOCB"
- "(%x).\n", __func__, ha->host_no, rval));
+ "(%x).\n", __func__, base_vha->host_no, rval));
printk("%s(%ld): failed to issue VP control IOCB"
- "(%x).\n", __func__, ha->host_no, rval);
+ "(%x).\n", __func__, base_vha->host_no, rval);
} else if (vce->entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, ha->host_no,
+ "-- error status (%x).\n", __func__, base_vha->host_no,
vce->entry_status));
printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, ha->host_no,
+ "-- error status (%x).\n", __func__, base_vha->host_no,
vce->entry_status);
rval = QLA_FUNCTION_FAILED;
} else if (vce->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- completion status (%x).\n", __func__, ha->host_no,
+ "-- completion status (%x).\n", __func__, base_vha->host_no,
le16_to_cpu(vce->comp_status)));
printk("%s(%ld): failed to complete IOCB "
- "-- completion status (%x).\n", __func__, ha->host_no,
+ "-- completion status (%x).\n", __func__, base_vha->host_no,
le16_to_cpu(vce->comp_status));
rval = QLA_FUNCTION_FAILED;
} else {
- DEBUG2(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG2(printk("%s(%ld): done.\n", __func__, base_vha->host_no));
}
dma_pool_free(ha->s_dma_pool, vce, vce_dma);
@@ -2863,7 +2901,7 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
*/
int
-qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,
+qla2x00_send_change_request(scsi_qla_host_t *vha, uint16_t format,
uint16_t vp_idx)
{
int rval;
@@ -2884,7 +2922,7 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,
mcp->in_mb = MBX_0|MBX_1;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval == QLA_SUCCESS) {
if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
@@ -2897,16 +2935,16 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,
}
int
-qla2x00_dump_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t addr,
+qla2x00_dump_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr,
uint32_t size)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
- if (MSW(addr) || IS_FWI2_CAPABLE(ha)) {
+ if (MSW(addr) || IS_FWI2_CAPABLE(vha->hw)) {
mcp->mb[0] = MBC_DUMP_RISC_RAM_EXTENDED;
mcp->mb[8] = MSW(addr);
mcp->out_mb = MBX_8|MBX_0;
@@ -2920,7 +2958,7 @@ qla2x00_dump_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t addr,
mcp->mb[6] = MSW(MSD(req_dma));
mcp->mb[7] = LSW(MSD(req_dma));
mcp->out_mb |= MBX_7|MBX_6|MBX_3|MBX_2|MBX_1;
- if (IS_FWI2_CAPABLE(ha)) {
+ if (IS_FWI2_CAPABLE(vha->hw)) {
mcp->mb[4] = MSW(size);
mcp->mb[5] = LSW(size);
mcp->out_mb |= MBX_5|MBX_4;
@@ -2932,13 +2970,13 @@ qla2x00_dump_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t addr,
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
- ha->host_no, rval, mcp->mb[0]));
+ vha->host_no, rval, mcp->mb[0]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -2954,20 +2992,21 @@ struct cs84xx_mgmt_cmd {
};
int
-qla84xx_verify_chip(struct scsi_qla_host *ha, uint16_t *status)
+qla84xx_verify_chip(struct scsi_qla_host *vha, uint16_t *status)
{
int rval, retry;
struct cs84xx_mgmt_cmd *mn;
dma_addr_t mn_dma;
uint16_t options;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
- DEBUG16(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG16(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
if (mn == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Verify ISP84XX "
- "IOCB.\n", __func__, ha->host_no));
+ "IOCB.\n", __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
@@ -2986,19 +3025,19 @@ qla84xx_verify_chip(struct scsi_qla_host *ha, uint16_t *status)
mn->p.req.options = cpu_to_le16(options);
DEBUG16(printk("%s(%ld): Dump of Verify Request.\n", __func__,
- ha->host_no));
+ vha->host_no));
DEBUG16(qla2x00_dump_buffer((uint8_t *)mn,
sizeof(*mn)));
- rval = qla2x00_issue_iocb_timeout(ha, mn, mn_dma, 0, 120);
+ rval = qla2x00_issue_iocb_timeout(vha, mn, mn_dma, 0, 120);
if (rval != QLA_SUCCESS) {
DEBUG2_16(printk("%s(%ld): failed to issue Verify "
- "IOCB (%x).\n", __func__, ha->host_no, rval));
+ "IOCB (%x).\n", __func__, vha->host_no, rval));
goto verify_done;
}
DEBUG16(printk("%s(%ld): Dump of Verify Response.\n", __func__,
- ha->host_no));
+ vha->host_no));
DEBUG16(qla2x00_dump_buffer((uint8_t *)mn,
sizeof(*mn)));
@@ -3006,21 +3045,21 @@ qla84xx_verify_chip(struct scsi_qla_host *ha, uint16_t *status)
status[1] = status[0] == CS_VCS_CHIP_FAILURE ?
le16_to_cpu(mn->p.rsp.failure_code) : 0;
DEBUG2_16(printk("%s(%ld): cs=%x fc=%x\n", __func__,
- ha->host_no, status[0], status[1]));
+ vha->host_no, status[0], status[1]));
if (status[0] != CS_COMPLETE) {
rval = QLA_FUNCTION_FAILED;
if (!(options & VCO_DONT_UPDATE_FW)) {
DEBUG2_16(printk("%s(%ld): Firmware update "
"failed. Retrying without update "
- "firmware.\n", __func__, ha->host_no));
+ "firmware.\n", __func__, vha->host_no));
options |= VCO_DONT_UPDATE_FW;
options &= ~VCO_FORCE_UPDATE;
retry = 1;
}
} else {
DEBUG2_16(printk("%s(%ld): firmware updated to %x.\n",
- __func__, ha->host_no,
+ __func__, vha->host_no,
le32_to_cpu(mn->p.rsp.fw_ver)));
/* NOTE: we only update OP firmware. */
@@ -3037,10 +3076,115 @@ verify_done:
if (rval != QLA_SUCCESS) {
DEBUG2_16(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
- DEBUG16(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG16(printk("%s(%ld): done.\n", __func__, vha->host_no));
+ }
+
+ return rval;
+}
+
+int
+qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req,
+ uint8_t options)
+{
+ int rval;
+ unsigned long flags;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct device_reg_25xxmq __iomem *reg;
+ struct qla_hw_data *ha = vha->hw;
+
+ mcp->mb[0] = MBC_INITIALIZE_MULTIQ;
+ mcp->mb[1] = options;
+ mcp->mb[2] = MSW(LSD(req->dma));
+ mcp->mb[3] = LSW(LSD(req->dma));
+ mcp->mb[6] = MSW(MSD(req->dma));
+ mcp->mb[7] = LSW(MSD(req->dma));
+ mcp->mb[5] = req->length;
+ if (req->rsp)
+ mcp->mb[10] = req->rsp->id;
+ mcp->mb[12] = req->qos;
+ mcp->mb[11] = req->vp_idx;
+ mcp->mb[13] = req->rid;
+
+ reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) +
+ QLA_QUE_PAGE * req->id);
+
+ mcp->mb[4] = req->id;
+ /* que in ptr index */
+ mcp->mb[8] = 0;
+ /* que out ptr index */
+ mcp->mb[9] = 0;
+ mcp->out_mb = MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8|MBX_7|
+ MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->flags = MBX_DMA_OUT;
+ mcp->tov = 60;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ if (!(options & BIT_0)) {
+ WRT_REG_DWORD(&reg->req_q_in, 0);
+ WRT_REG_DWORD(&reg->req_q_out, 0);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS)
+ DEBUG2_3_11(printk(KERN_WARNING "%s(%ld): failed=%x mb0=%x.\n",
+ __func__, vha->host_no, rval, mcp->mb[0]));
+ return rval;
+}
+
+int
+qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp,
+ uint8_t options)
+{
+ int rval;
+ unsigned long flags;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct device_reg_25xxmq __iomem *reg;
+ struct qla_hw_data *ha = vha->hw;
+
+ mcp->mb[0] = MBC_INITIALIZE_MULTIQ;
+ mcp->mb[1] = options;
+ mcp->mb[2] = MSW(LSD(rsp->dma));
+ mcp->mb[3] = LSW(LSD(rsp->dma));
+ mcp->mb[6] = MSW(MSD(rsp->dma));
+ mcp->mb[7] = LSW(MSD(rsp->dma));
+ mcp->mb[5] = rsp->length;
+ mcp->mb[11] = rsp->vp_idx;
+ mcp->mb[14] = rsp->msix->vector;
+ mcp->mb[13] = rsp->rid;
+
+ reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) +
+ QLA_QUE_PAGE * rsp->id);
+
+ mcp->mb[4] = rsp->id;
+ /* que in ptr index */
+ mcp->mb[8] = 0;
+ /* que out ptr index */
+ mcp->mb[9] = 0;
+ mcp->out_mb = MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8|MBX_7
+ |MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->flags = MBX_DMA_OUT;
+ mcp->tov = 60;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ if (!(options & BIT_0)) {
+ WRT_REG_DWORD(&reg->rsp_q_out, 0);
+ WRT_REG_DWORD(&reg->rsp_q_in, 0);
}
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS)
+ DEBUG2_3_11(printk(KERN_WARNING "%s(%ld): failed=%x "
+ "mb0=%x.\n", __func__,
+ vha->host_no, rval, mcp->mb[0]));
return rval;
}
+
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index 93560cd72784..386ffeae5b5a 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -5,6 +5,7 @@
* See LICENSE.qla2xxx for copyright and licensing details.
*/
#include "qla_def.h"
+#include "qla_gbl.h"
#include <linux/moduleparam.h>
#include <linux/vmalloc.h>
@@ -18,7 +19,7 @@
void
qla2x00_vp_stop_timer(scsi_qla_host_t *vha)
{
- if (vha->parent && vha->timer_active) {
+ if (vha->vp_idx && vha->timer_active) {
del_timer_sync(&vha->timer);
vha->timer_active = 0;
}
@@ -28,7 +29,7 @@ static uint32_t
qla24xx_allocate_vp_id(scsi_qla_host_t *vha)
{
uint32_t vp_id;
- scsi_qla_host_t *ha = vha->parent;
+ struct qla_hw_data *ha = vha->hw;
/* Find an empty slot and assign an vp_id */
mutex_lock(&ha->vport_lock);
@@ -44,7 +45,7 @@ qla24xx_allocate_vp_id(scsi_qla_host_t *vha)
ha->num_vhosts++;
ha->cur_vport_count++;
vha->vp_idx = vp_id;
- list_add_tail(&vha->vp_list, &ha->vp_list);
+ list_add_tail(&vha->list, &ha->vp_list);
mutex_unlock(&ha->vport_lock);
return vp_id;
}
@@ -53,24 +54,24 @@ void
qla24xx_deallocate_vp_id(scsi_qla_host_t *vha)
{
uint16_t vp_id;
- scsi_qla_host_t *ha = vha->parent;
+ struct qla_hw_data *ha = vha->hw;
mutex_lock(&ha->vport_lock);
vp_id = vha->vp_idx;
ha->num_vhosts--;
ha->cur_vport_count--;
clear_bit(vp_id, ha->vp_idx_map);
- list_del(&vha->vp_list);
+ list_del(&vha->list);
mutex_unlock(&ha->vport_lock);
}
static scsi_qla_host_t *
-qla24xx_find_vhost_by_name(scsi_qla_host_t *ha, uint8_t *port_name)
+qla24xx_find_vhost_by_name(struct qla_hw_data *ha, uint8_t *port_name)
{
scsi_qla_host_t *vha;
/* Locate matching device in database. */
- list_for_each_entry(vha, &ha->vp_list, vp_list) {
+ list_for_each_entry(vha, &ha->vp_list, list) {
if (!memcmp(port_name, vha->port_name, WWN_SIZE))
return vha;
}
@@ -94,16 +95,13 @@ static void
qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha)
{
fc_port_t *fcport;
- scsi_qla_host_t *pha = to_qla_parent(vha);
-
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->vp_idx != vha->vp_idx)
- continue;
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
DEBUG15(printk("scsi(%ld): Marking port dead, "
"loop_id=0x%04x :%x\n",
vha->host_no, fcport->loop_id, fcport->vp_idx));
+ atomic_set(&fcport->state, FCS_DEVICE_DEAD);
qla2x00_mark_device_lost(vha, fcport, 0, 0);
atomic_set(&fcport->state, FCS_UNCONFIGURED);
}
@@ -118,7 +116,6 @@ qla24xx_disable_vp(scsi_qla_host_t *vha)
atomic_set(&vha->loop_state, LOOP_DOWN);
atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
- /* Delete all vp's fcports from parent's list */
qla2x00_mark_vp_devices_dead(vha);
atomic_set(&vha->vp_state, VP_FAILED);
vha->flags.management_server_logged_in = 0;
@@ -135,11 +132,12 @@ int
qla24xx_enable_vp(scsi_qla_host_t *vha)
{
int ret;
- scsi_qla_host_t *ha = vha->parent;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
/* Check if physical ha port is Up */
- if (atomic_read(&ha->loop_state) == LOOP_DOWN ||
- atomic_read(&ha->loop_state) == LOOP_DEAD ) {
+ if (atomic_read(&base_vha->loop_state) == LOOP_DOWN ||
+ atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
vha->vp_err_state = VP_ERR_PORTDWN;
fc_vport_set_state(vha->fc_vport, FC_VPORT_LINKDOWN);
goto enable_failed;
@@ -177,8 +175,8 @@ qla24xx_configure_vp(scsi_qla_host_t *vha)
vha->host_no, __func__));
ret = qla2x00_send_change_request(vha, 0x3, vha->vp_idx);
if (ret != QLA_SUCCESS) {
- DEBUG15(qla_printk(KERN_ERR, vha, "Failed to enable receiving"
- " of RSCN requests: 0x%x\n", ret));
+ DEBUG15(qla_printk(KERN_ERR, vha->hw, "Failed to enable "
+ "receiving of RSCN requests: 0x%x\n", ret));
return;
} else {
/* Corresponds to SCR enabled */
@@ -194,25 +192,14 @@ qla24xx_configure_vp(scsi_qla_host_t *vha)
}
void
-qla2x00_alert_all_vps(scsi_qla_host_t *ha, uint16_t *mb)
+qla2x00_alert_all_vps(struct rsp_que *rsp, uint16_t *mb)
{
- int i, vp_idx_matched;
scsi_qla_host_t *vha;
+ struct qla_hw_data *ha = rsp->hw;
+ int i = 0;
- if (ha->parent)
- return;
-
- for_each_mapped_vp_idx(ha, i) {
- vp_idx_matched = 0;
-
- list_for_each_entry(vha, &ha->vp_list, vp_list) {
- if (i == vha->vp_idx) {
- vp_idx_matched = 1;
- break;
- }
- }
-
- if (vp_idx_matched) {
+ list_for_each_entry(vha, &ha->vp_list, list) {
+ if (vha->vp_idx) {
switch (mb[0]) {
case MBA_LIP_OCCURRED:
case MBA_LOOP_UP:
@@ -223,16 +210,17 @@ qla2x00_alert_all_vps(scsi_qla_host_t *ha, uint16_t *mb)
case MBA_PORT_UPDATE:
case MBA_RSCN_UPDATE:
DEBUG15(printk("scsi(%ld)%s: Async_event for"
- " VP[%d], mb = 0x%x, vha=%p\n",
- vha->host_no, __func__,i, *mb, vha));
- qla2x00_async_event(vha, mb);
+ " VP[%d], mb = 0x%x, vha=%p\n",
+ vha->host_no, __func__, i, *mb, vha));
+ qla2x00_async_event(vha, rsp, mb);
break;
}
}
+ i++;
}
}
-void
+int
qla2x00_vp_abort_isp(scsi_qla_host_t *vha)
{
/*
@@ -247,38 +235,56 @@ qla2x00_vp_abort_isp(scsi_qla_host_t *vha)
atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
}
+ /* To exclusively reset vport, we need to log it out first.*/
+ if (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
+ qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL);
+
DEBUG15(printk("scsi(%ld): Scheduling enable of Vport %d...\n",
vha->host_no, vha->vp_idx));
- qla24xx_enable_vp(vha);
+ return qla24xx_enable_vp(vha);
}
static int
qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
{
- scsi_qla_host_t *ha = vha->parent;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
if (test_and_clear_bit(VP_IDX_ACQUIRED, &vha->vp_flags)) {
/* VP acquired. complete port configuration */
- if (atomic_read(&ha->loop_state) == LOOP_READY) {
+ if (atomic_read(&base_vha->loop_state) == LOOP_READY) {
qla24xx_configure_vp(vha);
} else {
set_bit(VP_IDX_ACQUIRED, &vha->vp_flags);
- set_bit(VP_DPC_NEEDED, &ha->dpc_flags);
+ set_bit(VP_DPC_NEEDED, &base_vha->dpc_flags);
}
return 0;
}
- if (test_and_clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
- qla2x00_vp_abort_isp(vha);
+ if (test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags)) {
+ qla2x00_update_fcports(vha);
+ clear_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags);
+ }
+
+ if ((test_and_clear_bit(RELOGIN_NEEDED, &vha->dpc_flags)) &&
+ !test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) &&
+ atomic_read(&vha->loop_state) != LOOP_DOWN) {
+
+ DEBUG(printk("scsi(%ld): qla2x00_port_login()\n",
+ vha->host_no));
+ qla2x00_relogin(vha);
+
+ DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n",
+ vha->host_no));
+ }
if (test_and_clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) &&
(!(test_and_set_bit(RESET_ACTIVE, &vha->dpc_flags)))) {
clear_bit(RESET_ACTIVE, &vha->dpc_flags);
}
- if (atomic_read(&vha->vp_state) == VP_ACTIVE &&
- test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
+ if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags))) {
qla2x00_loop_resync(vha);
clear_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags);
@@ -289,38 +295,30 @@ qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
}
void
-qla2x00_do_dpc_all_vps(scsi_qla_host_t *ha)
+qla2x00_do_dpc_all_vps(scsi_qla_host_t *vha)
{
int ret;
- int i, vp_idx_matched;
- scsi_qla_host_t *vha;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *vp;
- if (ha->parent)
+ if (vha->vp_idx)
return;
if (list_empty(&ha->vp_list))
return;
- clear_bit(VP_DPC_NEEDED, &ha->dpc_flags);
-
- for_each_mapped_vp_idx(ha, i) {
- vp_idx_matched = 0;
-
- list_for_each_entry(vha, &ha->vp_list, vp_list) {
- if (i == vha->vp_idx) {
- vp_idx_matched = 1;
- break;
- }
- }
+ clear_bit(VP_DPC_NEEDED, &vha->dpc_flags);
- if (vp_idx_matched)
- ret = qla2x00_do_dpc_vp(vha);
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ if (vp->vp_idx)
+ ret = qla2x00_do_dpc_vp(vp);
}
}
int
qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport)
{
- scsi_qla_host_t *ha = shost_priv(fc_vport->shost);
+ scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost);
+ struct qla_hw_data *ha = base_vha->hw;
scsi_qla_host_t *vha;
uint8_t port_name[WWN_SIZE];
@@ -337,7 +335,7 @@ qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport)
/* Check up unique WWPN */
u64_to_wwn(fc_vport->port_name, port_name);
- if (!memcmp(port_name, ha->port_name, WWN_SIZE))
+ if (!memcmp(port_name, base_vha->port_name, WWN_SIZE))
return VPCERR_BAD_WWN;
vha = qla24xx_find_vhost_by_name(ha, port_name);
if (vha)
@@ -346,7 +344,7 @@ qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport)
/* Check up max-npiv-supports */
if (ha->num_vhosts > ha->max_npiv_vports) {
DEBUG15(printk("scsi(%ld): num_vhosts %ud is bigger than "
- "max_npv_vports %ud.\n", ha->host_no,
+ "max_npv_vports %ud.\n", base_vha->host_no,
ha->num_vhosts, ha->max_npiv_vports));
return VPCERR_UNSUPPORTED;
}
@@ -356,59 +354,34 @@ qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport)
scsi_qla_host_t *
qla24xx_create_vhost(struct fc_vport *fc_vport)
{
- scsi_qla_host_t *ha = shost_priv(fc_vport->shost);
+ scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost);
+ struct qla_hw_data *ha = base_vha->hw;
scsi_qla_host_t *vha;
+ struct scsi_host_template *sht = &qla24xx_driver_template;
struct Scsi_Host *host;
- host = scsi_host_alloc(&qla24xx_driver_template,
- sizeof(scsi_qla_host_t));
- if (!host) {
- printk(KERN_WARNING
- "qla2xxx: scsi_host_alloc() failed for vport\n");
+ vha = qla2x00_create_host(sht, ha);
+ if (!vha) {
+ DEBUG(printk("qla2xxx: scsi_host_alloc() failed for vport\n"));
return(NULL);
}
- vha = shost_priv(host);
-
- /* clone the parent hba */
- memcpy(vha, ha, sizeof (scsi_qla_host_t));
-
+ host = vha->host;
fc_vport->dd_data = vha;
-
- vha->node_name = kmalloc(WWN_SIZE * sizeof(char), GFP_KERNEL);
- if (!vha->node_name)
- goto create_vhost_failed_1;
-
- vha->port_name = kmalloc(WWN_SIZE * sizeof(char), GFP_KERNEL);
- if (!vha->port_name)
- goto create_vhost_failed_2;
-
/* New host info */
u64_to_wwn(fc_vport->node_name, vha->node_name);
u64_to_wwn(fc_vport->port_name, vha->port_name);
- vha->host = host;
- vha->host_no = host->host_no;
- vha->parent = ha;
vha->fc_vport = fc_vport;
vha->device_flags = 0;
vha->vp_idx = qla24xx_allocate_vp_id(vha);
if (vha->vp_idx > ha->max_npiv_vports) {
DEBUG15(printk("scsi(%ld): Couldn't allocate vp_id.\n",
vha->host_no));
- goto create_vhost_failed_3;
+ goto create_vhost_failed;
}
vha->mgmt_svr_loop_id = 10 + vha->vp_idx;
- init_completion(&vha->mbx_cmd_comp);
- complete(&vha->mbx_cmd_comp);
- init_completion(&vha->mbx_intr_comp);
-
- INIT_LIST_HEAD(&vha->list);
- INIT_LIST_HEAD(&vha->fcports);
- INIT_LIST_HEAD(&vha->vp_fcports);
- INIT_LIST_HEAD(&vha->work_list);
-
vha->dpc_flags = 0L;
set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
@@ -423,7 +396,9 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
qla2x00_start_timer(vha, qla2x00_timer, WATCH_INTERVAL);
- host->can_queue = vha->request_q_length + 128;
+ memset(vha->req_ques, 0, sizeof(vha->req_ques) * QLA_MAX_HOST_QUES);
+ vha->req_ques[0] = ha->req_q_map[0]->id;
+ host->can_queue = ha->req_q_map[0]->length + 128;
host->this_id = 255;
host->cmd_per_lun = 3;
host->max_cmd_len = MAX_CMDSZ;
@@ -440,12 +415,341 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
return vha;
-create_vhost_failed_3:
- kfree(vha->port_name);
+create_vhost_failed:
+ return NULL;
+}
-create_vhost_failed_2:
- kfree(vha->node_name);
+static void
+qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t que_id = req->id;
+
+ dma_free_coherent(&ha->pdev->dev, (req->length + 1) *
+ sizeof(request_t), req->ring, req->dma);
+ req->ring = NULL;
+ req->dma = 0;
+ if (que_id) {
+ ha->req_q_map[que_id] = NULL;
+ mutex_lock(&ha->vport_lock);
+ clear_bit(que_id, ha->req_qid_map);
+ mutex_unlock(&ha->vport_lock);
+ }
+ kfree(req);
+ req = NULL;
+}
-create_vhost_failed_1:
- return NULL;
+static void
+qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t que_id = rsp->id;
+
+ if (rsp->msix && rsp->msix->have_irq) {
+ free_irq(rsp->msix->vector, rsp);
+ rsp->msix->have_irq = 0;
+ rsp->msix->rsp = NULL;
+ }
+ dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) *
+ sizeof(response_t), rsp->ring, rsp->dma);
+ rsp->ring = NULL;
+ rsp->dma = 0;
+ if (que_id) {
+ ha->rsp_q_map[que_id] = NULL;
+ mutex_lock(&ha->vport_lock);
+ clear_bit(que_id, ha->rsp_qid_map);
+ mutex_unlock(&ha->vport_lock);
+ }
+ kfree(rsp);
+ rsp = NULL;
+}
+
+int
+qla25xx_delete_req_que(struct scsi_qla_host *vha, struct req_que *req)
+{
+ int ret = -1;
+
+ if (req) {
+ req->options |= BIT_0;
+ ret = qla25xx_init_req_que(vha, req, req->options);
+ }
+ if (ret == QLA_SUCCESS)
+ qla25xx_free_req_que(vha, req);
+
+ return ret;
+}
+
+int
+qla25xx_delete_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
+{
+ int ret = -1;
+
+ if (rsp) {
+ rsp->options |= BIT_0;
+ ret = qla25xx_init_rsp_que(vha, rsp, rsp->options);
+ }
+ if (ret == QLA_SUCCESS)
+ qla25xx_free_rsp_que(vha, rsp);
+
+ return ret;
+}
+
+int qla25xx_update_req_que(struct scsi_qla_host *vha, uint8_t que, uint8_t qos)
+{
+ int ret = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[que];
+
+ req->options |= BIT_3;
+ req->qos = qos;
+ ret = qla25xx_init_req_que(vha, req, req->options);
+ if (ret != QLA_SUCCESS)
+ DEBUG2_17(printk(KERN_WARNING "%s failed\n", __func__));
+ /* restore options bit */
+ req->options &= ~BIT_3;
+ return ret;
+}
+
+
+/* Delete all queues for a given vhost */
+int
+qla25xx_delete_queues(struct scsi_qla_host *vha, uint8_t que_no)
+{
+ int cnt, ret = 0;
+ struct req_que *req = NULL;
+ struct rsp_que *rsp = NULL;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (que_no) {
+ /* Delete request queue */
+ req = ha->req_q_map[que_no];
+ if (req) {
+ rsp = req->rsp;
+ ret = qla25xx_delete_req_que(vha, req);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Couldn't delete req que %d\n", req->id);
+ return ret;
+ }
+ /* Delete associated response queue */
+ if (rsp) {
+ ret = qla25xx_delete_rsp_que(vha, rsp);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Couldn't delete rsp que %d\n",
+ rsp->id);
+ return ret;
+ }
+ }
+ }
+ } else { /* delete all queues of this host */
+ for (cnt = 0; cnt < QLA_MAX_HOST_QUES; cnt++) {
+ /* Delete request queues */
+ req = ha->req_q_map[vha->req_ques[cnt]];
+ if (req && req->id) {
+ rsp = req->rsp;
+ ret = qla25xx_delete_req_que(vha, req);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Couldn't delete req que %d\n",
+ vha->req_ques[cnt]);
+ return ret;
+ }
+ vha->req_ques[cnt] = ha->req_q_map[0]->id;
+ /* Delete associated response queue */
+ if (rsp && rsp->id) {
+ ret = qla25xx_delete_rsp_que(vha, rsp);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Couldn't delete rsp que %d\n",
+ rsp->id);
+ return ret;
+ }
+ }
+ }
+ }
+ }
+ qla_printk(KERN_INFO, ha, "Queues deleted for vport:%d\n",
+ vha->vp_idx);
+ return ret;
+}
+
+int
+qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
+ uint8_t vp_idx, uint16_t rid, uint8_t rsp_que, uint8_t qos)
+{
+ int ret = 0;
+ struct req_que *req = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ uint16_t que_id = 0;
+
+ req = kzalloc(sizeof(struct req_que), GFP_KERNEL);
+ if (req == NULL) {
+ qla_printk(KERN_WARNING, ha, "could not allocate memory"
+ "for request que\n");
+ goto que_failed;
+ }
+
+ req->length = REQUEST_ENTRY_CNT_24XX;
+ req->ring = dma_alloc_coherent(&ha->pdev->dev,
+ (req->length + 1) * sizeof(request_t),
+ &req->dma, GFP_KERNEL);
+ if (req->ring == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - request_ring\n");
+ goto que_failed;
+ }
+
+ mutex_lock(&ha->vport_lock);
+ que_id = find_first_zero_bit(ha->req_qid_map, ha->max_queues);
+ if (que_id >= ha->max_queues) {
+ mutex_unlock(&ha->vport_lock);
+ qla_printk(KERN_INFO, ha, "No resources to create "
+ "additional request queue\n");
+ goto que_failed;
+ }
+ set_bit(que_id, ha->req_qid_map);
+ ha->req_q_map[que_id] = req;
+ req->rid = rid;
+ req->vp_idx = vp_idx;
+ req->qos = qos;
+
+ if (ha->rsp_q_map[rsp_que])
+ req->rsp = ha->rsp_q_map[rsp_que];
+ /* Use alternate PCI bus number */
+ if (MSB(req->rid))
+ options |= BIT_4;
+ /* Use alternate PCI devfn */
+ if (LSB(req->rid))
+ options |= BIT_5;
+ req->options = options;
+ req->ring_ptr = req->ring;
+ req->ring_index = 0;
+ req->cnt = req->length;
+ req->id = que_id;
+ mutex_unlock(&ha->vport_lock);
+
+ ret = qla25xx_init_req_que(base_vha, req, options);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha, "%s failed\n", __func__);
+ mutex_lock(&ha->vport_lock);
+ clear_bit(que_id, ha->req_qid_map);
+ mutex_unlock(&ha->vport_lock);
+ goto que_failed;
+ }
+
+ return req->id;
+
+que_failed:
+ qla25xx_free_req_que(base_vha, req);
+ return 0;
+}
+
+/* create response queue */
+int
+qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
+ uint8_t vp_idx, uint16_t rid)
+{
+ int ret = 0;
+ struct rsp_que *rsp = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ uint16_t que_id = 0;;
+
+ rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL);
+ if (rsp == NULL) {
+ qla_printk(KERN_WARNING, ha, "could not allocate memory for"
+ " response que\n");
+ goto que_failed;
+ }
+
+ rsp->length = RESPONSE_ENTRY_CNT_2300;
+ rsp->ring = dma_alloc_coherent(&ha->pdev->dev,
+ (rsp->length + 1) * sizeof(response_t),
+ &rsp->dma, GFP_KERNEL);
+ if (rsp->ring == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - response_ring\n");
+ goto que_failed;
+ }
+
+ mutex_lock(&ha->vport_lock);
+ que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_queues);
+ if (que_id >= ha->max_queues) {
+ mutex_unlock(&ha->vport_lock);
+ qla_printk(KERN_INFO, ha, "No resources to create "
+ "additional response queue\n");
+ goto que_failed;
+ }
+ set_bit(que_id, ha->rsp_qid_map);
+
+ if (ha->flags.msix_enabled)
+ rsp->msix = &ha->msix_entries[que_id + 1];
+ else
+ qla_printk(KERN_WARNING, ha, "msix not enabled\n");
+
+ ha->rsp_q_map[que_id] = rsp;
+ rsp->rid = rid;
+ rsp->vp_idx = vp_idx;
+ rsp->hw = ha;
+ /* Use alternate PCI bus number */
+ if (MSB(rsp->rid))
+ options |= BIT_4;
+ /* Use alternate PCI devfn */
+ if (LSB(rsp->rid))
+ options |= BIT_5;
+ rsp->options = options;
+ rsp->ring_ptr = rsp->ring;
+ rsp->ring_index = 0;
+ rsp->id = que_id;
+ mutex_unlock(&ha->vport_lock);
+
+ ret = qla25xx_request_irq(rsp);
+ if (ret)
+ goto que_failed;
+
+ ret = qla25xx_init_rsp_que(base_vha, rsp, options);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha, "%s failed\n", __func__);
+ mutex_lock(&ha->vport_lock);
+ clear_bit(que_id, ha->rsp_qid_map);
+ mutex_unlock(&ha->vport_lock);
+ goto que_failed;
+ }
+
+ qla2x00_init_response_q_entries(rsp);
+
+ return rsp->id;
+
+que_failed:
+ qla25xx_free_rsp_que(base_vha, rsp);
+ return 0;
+}
+
+int
+qla25xx_create_queues(struct scsi_qla_host *vha, uint8_t qos)
+{
+ uint16_t options = 0;
+ uint8_t ret = 0;
+ struct qla_hw_data *ha = vha->hw;
+
+ options |= BIT_1;
+ ret = qla25xx_create_rsp_que(ha, options, vha->vp_idx, 0);
+ if (!ret) {
+ qla_printk(KERN_WARNING, ha, "Response Que create failed\n");
+ return ret;
+ } else
+ qla_printk(KERN_INFO, ha, "Response Que:%d created.\n", ret);
+
+ options = 0;
+ if (qos & BIT_7)
+ options |= BIT_8;
+ ret = qla25xx_create_req_que(ha, options, vha->vp_idx, 0, ret,
+ qos & ~BIT_7);
+ if (ret) {
+ vha->req_ques[0] = ret;
+ qla_printk(KERN_INFO, ha, "Request Que:%d created.\n", ret);
+ } else
+ qla_printk(KERN_WARNING, ha, "Request Que create failed\n");
+
+ return ret;
}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 35567203ef61..8ea927788b3f 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -92,7 +92,12 @@ MODULE_PARM_DESC(ql2xiidmaenable,
"Enables iIDMA settings "
"Default is 1 - perform iIDMA. 0 - no iIDMA.");
-
+int ql2xmaxqueues = 1;
+module_param(ql2xmaxqueues, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xmaxqueues,
+ "Enables MQ settings "
+ "Default is 1 for single queue. Set it to number \
+ of queues in MQ mode.");
/*
* SCSI host template entry points
*/
@@ -183,42 +188,108 @@ struct scsi_transport_template *qla2xxx_transport_vport_template = NULL;
*/
__inline__ void
-qla2x00_start_timer(scsi_qla_host_t *ha, void *func, unsigned long interval)
+qla2x00_start_timer(scsi_qla_host_t *vha, void *func, unsigned long interval)
{
- init_timer(&ha->timer);
- ha->timer.expires = jiffies + interval * HZ;
- ha->timer.data = (unsigned long)ha;
- ha->timer.function = (void (*)(unsigned long))func;
- add_timer(&ha->timer);
- ha->timer_active = 1;
+ init_timer(&vha->timer);
+ vha->timer.expires = jiffies + interval * HZ;
+ vha->timer.data = (unsigned long)vha;
+ vha->timer.function = (void (*)(unsigned long))func;
+ add_timer(&vha->timer);
+ vha->timer_active = 1;
}
static inline void
-qla2x00_restart_timer(scsi_qla_host_t *ha, unsigned long interval)
+qla2x00_restart_timer(scsi_qla_host_t *vha, unsigned long interval)
{
- mod_timer(&ha->timer, jiffies + interval * HZ);
+ mod_timer(&vha->timer, jiffies + interval * HZ);
}
static __inline__ void
-qla2x00_stop_timer(scsi_qla_host_t *ha)
+qla2x00_stop_timer(scsi_qla_host_t *vha)
{
- del_timer_sync(&ha->timer);
- ha->timer_active = 0;
+ del_timer_sync(&vha->timer);
+ vha->timer_active = 0;
}
static int qla2x00_do_dpc(void *data);
static void qla2x00_rst_aen(scsi_qla_host_t *);
-static int qla2x00_mem_alloc(scsi_qla_host_t *);
-static void qla2x00_mem_free(scsi_qla_host_t *ha);
-static void qla2x00_sp_free_dma(scsi_qla_host_t *, srb_t *);
+static int qla2x00_mem_alloc(struct qla_hw_data *, uint16_t, uint16_t,
+ struct req_que **, struct rsp_que **);
+static void qla2x00_mem_free(struct qla_hw_data *);
+static void qla2x00_sp_free_dma(srb_t *);
/* -------------------------------------------------------------------------- */
+static int qla2x00_alloc_queues(struct qla_hw_data *ha)
+{
+ ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_queues,
+ GFP_KERNEL);
+ if (!ha->req_q_map) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for request queue ptrs\n");
+ goto fail_req_map;
+ }
+
+ ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_queues,
+ GFP_KERNEL);
+ if (!ha->rsp_q_map) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for response queue ptrs\n");
+ goto fail_rsp_map;
+ }
+ set_bit(0, ha->rsp_qid_map);
+ set_bit(0, ha->req_qid_map);
+ return 1;
+
+fail_rsp_map:
+ kfree(ha->req_q_map);
+ ha->req_q_map = NULL;
+fail_req_map:
+ return -ENOMEM;
+}
+
+static void qla2x00_free_que(struct qla_hw_data *ha, struct req_que *req,
+ struct rsp_que *rsp)
+{
+ if (rsp && rsp->ring)
+ dma_free_coherent(&ha->pdev->dev,
+ (rsp->length + 1) * sizeof(response_t),
+ rsp->ring, rsp->dma);
+
+ kfree(rsp);
+ rsp = NULL;
+ if (req && req->ring)
+ dma_free_coherent(&ha->pdev->dev,
+ (req->length + 1) * sizeof(request_t),
+ req->ring, req->dma);
+
+ kfree(req);
+ req = NULL;
+}
+
+static void qla2x00_free_queues(struct qla_hw_data *ha)
+{
+ struct req_que *req;
+ struct rsp_que *rsp;
+ int cnt;
+
+ for (cnt = 0; cnt < ha->max_queues; cnt++) {
+ rsp = ha->rsp_q_map[cnt];
+ req = ha->req_q_map[cnt];
+ qla2x00_free_que(ha, req, rsp);
+ }
+ kfree(ha->rsp_q_map);
+ ha->rsp_q_map = NULL;
+
+ kfree(ha->req_q_map);
+ ha->req_q_map = NULL;
+}
static char *
-qla2x00_pci_info_str(struct scsi_qla_host *ha, char *str)
+qla2x00_pci_info_str(struct scsi_qla_host *vha, char *str)
{
+ struct qla_hw_data *ha = vha->hw;
static char *pci_bus_modes[] = {
"33", "66", "100", "133",
};
@@ -240,9 +311,10 @@ qla2x00_pci_info_str(struct scsi_qla_host *ha, char *str)
}
static char *
-qla24xx_pci_info_str(struct scsi_qla_host *ha, char *str)
+qla24xx_pci_info_str(struct scsi_qla_host *vha, char *str)
{
static char *pci_bus_modes[] = { "33", "66", "100", "133", };
+ struct qla_hw_data *ha = vha->hw;
uint32_t pci_bus;
int pcie_reg;
@@ -290,9 +362,10 @@ qla24xx_pci_info_str(struct scsi_qla_host *ha, char *str)
}
static char *
-qla2x00_fw_version_str(struct scsi_qla_host *ha, char *str)
+qla2x00_fw_version_str(struct scsi_qla_host *vha, char *str)
{
char un_str[10];
+ struct qla_hw_data *ha = vha->hw;
sprintf(str, "%d.%02d.%02d ", ha->fw_major_version,
ha->fw_minor_version,
@@ -328,8 +401,9 @@ qla2x00_fw_version_str(struct scsi_qla_host *ha, char *str)
}
static char *
-qla24xx_fw_version_str(struct scsi_qla_host *ha, char *str)
+qla24xx_fw_version_str(struct scsi_qla_host *vha, char *str)
{
+ struct qla_hw_data *ha = vha->hw;
sprintf(str, "%d.%02d.%02d ", ha->fw_major_version,
ha->fw_minor_version,
ha->fw_subminor_version);
@@ -354,18 +428,20 @@ qla24xx_fw_version_str(struct scsi_qla_host *ha, char *str)
}
static inline srb_t *
-qla2x00_get_new_sp(scsi_qla_host_t *ha, fc_port_t *fcport,
+qla2x00_get_new_sp(scsi_qla_host_t *vha, fc_port_t *fcport,
struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
{
srb_t *sp;
+ struct qla_hw_data *ha = vha->hw;
sp = mempool_alloc(ha->srb_mempool, GFP_ATOMIC);
if (!sp)
return sp;
- sp->ha = ha;
+ sp->vha = vha;
sp->fcport = fcport;
sp->cmd = cmd;
+ sp->que = ha->req_q_map[0];
sp->flags = 0;
CMD_SP(cmd) = (void *)sp;
cmd->scsi_done = done;
@@ -376,9 +452,10 @@ qla2x00_get_new_sp(scsi_qla_host_t *ha, fc_port_t *fcport,
static int
qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
+ struct qla_hw_data *ha = vha->hw;
srb_t *sp;
int rval;
@@ -399,33 +476,33 @@ qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
if (atomic_read(&fcport->state) != FCS_ONLINE) {
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
- atomic_read(&ha->loop_state) == LOOP_DEAD) {
+ atomic_read(&vha->loop_state) == LOOP_DEAD) {
cmd->result = DID_NO_CONNECT << 16;
goto qc_fail_command;
}
goto qc_target_busy;
}
- spin_unlock_irq(ha->host->host_lock);
+ spin_unlock_irq(vha->host->host_lock);
- sp = qla2x00_get_new_sp(ha, fcport, cmd, done);
+ sp = qla2x00_get_new_sp(vha, fcport, cmd, done);
if (!sp)
goto qc_host_busy_lock;
- rval = qla2x00_start_scsi(sp);
+ rval = ha->isp_ops->start_scsi(sp);
if (rval != QLA_SUCCESS)
goto qc_host_busy_free_sp;
- spin_lock_irq(ha->host->host_lock);
+ spin_lock_irq(vha->host->host_lock);
return 0;
qc_host_busy_free_sp:
- qla2x00_sp_free_dma(ha, sp);
+ qla2x00_sp_free_dma(sp);
mempool_free(sp, ha->srb_mempool);
qc_host_busy_lock:
- spin_lock_irq(ha->host->host_lock);
+ spin_lock_irq(vha->host->host_lock);
return SCSI_MLQUEUE_HOST_BUSY;
qc_target_busy:
@@ -441,14 +518,15 @@ qc_fail_command:
static int
qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
srb_t *sp;
int rval;
- scsi_qla_host_t *pha = to_qla_parent(ha);
- if (unlikely(pci_channel_offline(pha->pdev))) {
+ if (unlikely(pci_channel_offline(ha->pdev))) {
cmd->result = DID_REQUEUE << 16;
goto qc24_fail_command;
}
@@ -465,33 +543,33 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
if (atomic_read(&fcport->state) != FCS_ONLINE) {
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
- atomic_read(&pha->loop_state) == LOOP_DEAD) {
+ atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
cmd->result = DID_NO_CONNECT << 16;
goto qc24_fail_command;
}
goto qc24_target_busy;
}
- spin_unlock_irq(ha->host->host_lock);
+ spin_unlock_irq(vha->host->host_lock);
- sp = qla2x00_get_new_sp(pha, fcport, cmd, done);
+ sp = qla2x00_get_new_sp(base_vha, fcport, cmd, done);
if (!sp)
goto qc24_host_busy_lock;
- rval = qla24xx_start_scsi(sp);
+ rval = ha->isp_ops->start_scsi(sp);
if (rval != QLA_SUCCESS)
goto qc24_host_busy_free_sp;
- spin_lock_irq(ha->host->host_lock);
+ spin_lock_irq(vha->host->host_lock);
return 0;
qc24_host_busy_free_sp:
- qla2x00_sp_free_dma(pha, sp);
- mempool_free(sp, pha->srb_mempool);
+ qla2x00_sp_free_dma(sp);
+ mempool_free(sp, ha->srb_mempool);
qc24_host_busy_lock:
- spin_lock_irq(ha->host->host_lock);
+ spin_lock_irq(vha->host->host_lock);
return SCSI_MLQUEUE_HOST_BUSY;
qc24_target_busy:
@@ -510,17 +588,14 @@ qc24_fail_command:
* max time.
*
* Input:
- * ha = actual ha whose done queue will contain the command
- * returned by firmware.
* cmd = Scsi Command to wait on.
- * flag = Abort/Reset(Bus or Device Reset)
*
* Return:
* Not Found : 0
* Found : 1
*/
static int
-qla2x00_eh_wait_on_command(scsi_qla_host_t *ha, struct scsi_cmnd *cmd)
+qla2x00_eh_wait_on_command(struct scsi_cmnd *cmd)
{
#define ABORT_POLLING_PERIOD 1000
#define ABORT_WAIT_ITER ((10 * 1000) / (ABORT_POLLING_PERIOD))
@@ -557,21 +632,22 @@ qla2x00_eh_wait_on_command(scsi_qla_host_t *ha, struct scsi_cmnd *cmd)
* Failed (Adapter is offline/disabled) : 1
*/
int
-qla2x00_wait_for_hba_online(scsi_qla_host_t *ha)
+qla2x00_wait_for_hba_online(scsi_qla_host_t *vha)
{
int return_status;
unsigned long wait_online;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ);
- while (((test_bit(ISP_ABORT_NEEDED, &pha->dpc_flags)) ||
- test_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags) ||
- test_bit(ISP_ABORT_RETRY, &pha->dpc_flags) ||
- pha->dpc_active) && time_before(jiffies, wait_online)) {
+ while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) ||
+ test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) ||
+ test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) ||
+ ha->dpc_active) && time_before(jiffies, wait_online)) {
msleep(1000);
}
- if (pha->flags.online)
+ if (base_vha->flags.online)
return_status = QLA_SUCCESS;
else
return_status = QLA_FUNCTION_FAILED;
@@ -596,19 +672,20 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *ha)
* Failed (LOOP_NOT_READY) : 1
*/
static inline int
-qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha)
+qla2x00_wait_for_loop_ready(scsi_qla_host_t *vha)
{
int return_status = QLA_SUCCESS;
unsigned long loop_timeout ;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
/* wait for 5 min at the max for loop to be ready */
loop_timeout = jiffies + (MAX_LOOP_TIMEOUT * HZ);
- while ((!atomic_read(&pha->loop_down_timer) &&
- atomic_read(&pha->loop_state) == LOOP_DOWN) ||
- atomic_read(&pha->loop_state) != LOOP_READY) {
- if (atomic_read(&pha->loop_state) == LOOP_DEAD) {
+ while ((!atomic_read(&base_vha->loop_down_timer) &&
+ atomic_read(&base_vha->loop_state) == LOOP_DOWN) ||
+ atomic_read(&base_vha->loop_state) != LOOP_READY) {
+ if (atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
return_status = QLA_FUNCTION_FAILED;
break;
}
@@ -624,35 +701,42 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha)
void
qla2x00_abort_fcport_cmds(fc_port_t *fcport)
{
- int cnt;
+ int cnt, que, id;
unsigned long flags;
srb_t *sp;
- scsi_qla_host_t *ha = fcport->ha;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ scsi_qla_host_t *vha = fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
- spin_lock_irqsave(&pha->hardware_lock, flags);
- for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
- sp = pha->outstanding_cmds[cnt];
- if (!sp)
- continue;
- if (sp->fcport != fcport)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (que = 0; que < QLA_MAX_HOST_QUES; que++) {
+ id = vha->req_ques[que];
+ req = ha->req_q_map[id];
+ if (!req)
continue;
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = req->outstanding_cmds[cnt];
+ if (!sp)
+ continue;
+ if (sp->fcport != fcport)
+ continue;
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
- if (ha->isp_ops->abort_command(ha, sp)) {
- DEBUG2(qla_printk(KERN_WARNING, ha,
- "Abort failed -- %lx\n", sp->cmd->serial_number));
- } else {
- if (qla2x00_eh_wait_on_command(ha, sp->cmd) !=
- QLA_SUCCESS)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (ha->isp_ops->abort_command(vha, sp, req)) {
DEBUG2(qla_printk(KERN_WARNING, ha,
- "Abort failed while waiting -- %lx\n",
- sp->cmd->serial_number));
-
+ "Abort failed -- %lx\n",
+ sp->cmd->serial_number));
+ } else {
+ if (qla2x00_eh_wait_on_command(sp->cmd) !=
+ QLA_SUCCESS)
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "Abort failed while waiting -- %lx\n",
+ sp->cmd->serial_number));
+ }
+ spin_lock_irqsave(&ha->hardware_lock, flags);
}
- spin_lock_irqsave(&pha->hardware_lock, flags);
}
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
static void
@@ -690,14 +774,16 @@ qla2x00_block_error_handler(struct scsi_cmnd *cmnd)
static int
qla2xxx_eh_abort(struct scsi_cmnd *cmd)
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
srb_t *sp;
int ret, i;
unsigned int id, lun;
unsigned long serial;
unsigned long flags;
int wait = 0;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
+ srb_t *spt;
qla2x00_block_error_handler(cmd);
@@ -709,11 +795,15 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
id = cmd->device->id;
lun = cmd->device->lun;
serial = cmd->serial_number;
+ spt = (srb_t *) CMD_SP(cmd);
+ if (!spt)
+ return SUCCESS;
+ req = spt->que;
/* Check active list for command command. */
- spin_lock_irqsave(&pha->hardware_lock, flags);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
for (i = 1; i < MAX_OUTSTANDING_COMMANDS; i++) {
- sp = pha->outstanding_cmds[i];
+ sp = req->outstanding_cmds[i];
if (sp == NULL)
continue;
@@ -721,38 +811,36 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
if (sp->cmd != cmd)
continue;
- DEBUG2(printk("%s(%ld): aborting sp %p from RISC. pid=%ld.\n",
- __func__, ha->host_no, sp, serial));
+ DEBUG2(printk("%s(%ld): aborting sp %p from RISC."
+ " pid=%ld.\n", __func__, vha->host_no, sp, serial));
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
- if (ha->isp_ops->abort_command(ha, sp)) {
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (ha->isp_ops->abort_command(vha, sp, req)) {
DEBUG2(printk("%s(%ld): abort_command "
- "mbx failed.\n", __func__, ha->host_no));
- ret = FAILED;
+ "mbx failed.\n", __func__, vha->host_no));
} else {
DEBUG3(printk("%s(%ld): abort_command "
- "mbx success.\n", __func__, ha->host_no));
+ "mbx success.\n", __func__, vha->host_no));
wait = 1;
}
- spin_lock_irqsave(&pha->hardware_lock, flags);
-
+ spin_lock_irqsave(&ha->hardware_lock, flags);
break;
}
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
/* Wait for the command to be returned. */
if (wait) {
- if (qla2x00_eh_wait_on_command(ha, cmd) != QLA_SUCCESS) {
+ if (qla2x00_eh_wait_on_command(cmd) != QLA_SUCCESS) {
qla_printk(KERN_ERR, ha,
"scsi(%ld:%d:%d): Abort handler timed out -- %lx "
- "%x.\n", ha->host_no, id, lun, serial, ret);
+ "%x.\n", vha->host_no, id, lun, serial, ret);
ret = FAILED;
}
}
qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d): Abort command issued -- %d %lx %x.\n",
- ha->host_no, id, lun, wait, serial, ret);
+ vha->host_no, id, lun, wait, serial, ret);
return ret;
}
@@ -764,23 +852,27 @@ enum nexus_wait_type {
};
static int
-qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha, unsigned int t,
- unsigned int l, enum nexus_wait_type type)
+qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t,
+ unsigned int l, srb_t *sp, enum nexus_wait_type type)
{
int cnt, match, status;
- srb_t *sp;
unsigned long flags;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
status = QLA_SUCCESS;
- spin_lock_irqsave(&pha->hardware_lock, flags);
- for (cnt = 1; status == QLA_SUCCESS && cnt < MAX_OUTSTANDING_COMMANDS;
- cnt++) {
- sp = pha->outstanding_cmds[cnt];
+ if (!sp)
+ return status;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ req = sp->que;
+ for (cnt = 1; status == QLA_SUCCESS &&
+ cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = req->outstanding_cmds[cnt];
if (!sp)
continue;
- if (ha->vp_idx != sp->fcport->ha->vp_idx)
+ if (vha->vp_idx != sp->fcport->vha->vp_idx)
continue;
match = 0;
switch (type) {
@@ -792,17 +884,17 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha, unsigned int t,
break;
case WAIT_LUN:
match = (sp->cmd->device->id == t &&
- sp->cmd->device->lun == l);
+ sp->cmd->device->lun == l);
break;
}
if (!match)
continue;
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
- status = qla2x00_eh_wait_on_command(ha, sp->cmd);
- spin_lock_irqsave(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ status = qla2x00_eh_wait_on_command(sp->cmd);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
}
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
return status;
}
@@ -818,7 +910,7 @@ static int
__qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int))
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
int err;
@@ -827,31 +919,31 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
if (!fcport)
return FAILED;
- qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET ISSUED.\n",
- ha->host_no, cmd->device->id, cmd->device->lun, name);
+ qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET ISSUED.\n",
+ vha->host_no, cmd->device->id, cmd->device->lun, name);
err = 0;
- if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
goto eh_reset_failed;
err = 1;
- if (qla2x00_wait_for_loop_ready(ha) != QLA_SUCCESS)
+ if (qla2x00_wait_for_loop_ready(vha) != QLA_SUCCESS)
goto eh_reset_failed;
err = 2;
if (do_reset(fcport, cmd->device->lun) != QLA_SUCCESS)
goto eh_reset_failed;
err = 3;
- if (qla2x00_eh_wait_for_pending_commands(ha, cmd->device->id,
- cmd->device->lun, type) != QLA_SUCCESS)
+ if (qla2x00_eh_wait_for_pending_commands(vha, cmd->device->id,
+ cmd->device->lun, (srb_t *) CMD_SP(cmd), type) != QLA_SUCCESS)
goto eh_reset_failed;
- qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n",
- ha->host_no, cmd->device->id, cmd->device->lun, name);
+ qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n",
+ vha->host_no, cmd->device->id, cmd->device->lun, name);
return SUCCESS;
eh_reset_failed:
- qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n",
- ha->host_no, cmd->device->id, cmd->device->lun, name,
+ qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n"
+ , vha->host_no, cmd->device->id, cmd->device->lun, name,
reset_errors[err]);
return FAILED;
}
@@ -859,7 +951,8 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
static int
qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+ struct qla_hw_data *ha = vha->hw;
return __qla2xxx_eh_generic_reset("DEVICE", WAIT_LUN, cmd,
ha->isp_ops->lun_reset);
@@ -868,7 +961,8 @@ qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
static int
qla2xxx_eh_target_reset(struct scsi_cmnd *cmd)
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+ struct qla_hw_data *ha = vha->hw;
return __qla2xxx_eh_generic_reset("TARGET", WAIT_TARGET, cmd,
ha->isp_ops->target_reset);
@@ -892,12 +986,12 @@ qla2xxx_eh_target_reset(struct scsi_cmnd *cmd)
static int
qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
int ret = FAILED;
unsigned int id, lun;
unsigned long serial;
+ srb_t *sp = (srb_t *) CMD_SP(cmd);
qla2x00_block_error_handler(cmd);
@@ -908,28 +1002,28 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
if (!fcport)
return ret;
- qla_printk(KERN_INFO, ha,
- "scsi(%ld:%d:%d): LOOP RESET ISSUED.\n", ha->host_no, id, lun);
+ qla_printk(KERN_INFO, vha->hw,
+ "scsi(%ld:%d:%d): BUS RESET ISSUED.\n", vha->host_no, id, lun);
- if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) {
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
DEBUG2(printk("%s failed:board disabled\n",__func__));
goto eh_bus_reset_done;
}
- if (qla2x00_wait_for_loop_ready(ha) == QLA_SUCCESS) {
- if (qla2x00_loop_reset(ha) == QLA_SUCCESS)
+ if (qla2x00_wait_for_loop_ready(vha) == QLA_SUCCESS) {
+ if (qla2x00_loop_reset(vha) == QLA_SUCCESS)
ret = SUCCESS;
}
if (ret == FAILED)
goto eh_bus_reset_done;
/* Flush outstanding commands. */
- if (qla2x00_eh_wait_for_pending_commands(pha, 0, 0, WAIT_HOST) !=
+ if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, sp, WAIT_HOST) !=
QLA_SUCCESS)
ret = FAILED;
eh_bus_reset_done:
- qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__,
+ qla_printk(KERN_INFO, vha->hw, "%s: reset %s\n", __func__,
(ret == FAILED) ? "failed" : "succeded");
return ret;
@@ -953,12 +1047,14 @@ eh_bus_reset_done:
static int
qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
+ struct qla_hw_data *ha = vha->hw;
int ret = FAILED;
unsigned int id, lun;
unsigned long serial;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ srb_t *sp = (srb_t *) CMD_SP(cmd);
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
qla2x00_block_error_handler(cmd);
@@ -970,9 +1066,9 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
return ret;
qla_printk(KERN_INFO, ha,
- "scsi(%ld:%d:%d): ADAPTER RESET ISSUED.\n", ha->host_no, id, lun);
+ "scsi(%ld:%d:%d): ADAPTER RESET ISSUED.\n", vha->host_no, id, lun);
- if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
goto eh_host_reset_lock;
/*
@@ -983,26 +1079,28 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
* devices as lost kicking of the port_down_timer
* while dpc is stuck for the mailbox to complete.
*/
- qla2x00_wait_for_loop_ready(ha);
- set_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags);
- if (qla2x00_abort_isp(pha)) {
- clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags);
- /* failed. schedule dpc to try */
- set_bit(ISP_ABORT_NEEDED, &pha->dpc_flags);
-
- if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
+ qla2x00_wait_for_loop_ready(vha);
+ if (vha != base_vha) {
+ if (qla2x00_vp_abort_isp(vha))
goto eh_host_reset_lock;
+ } else {
+ set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ if (qla2x00_abort_isp(base_vha)) {
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ /* failed. schedule dpc to try */
+ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
+
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
+ goto eh_host_reset_lock;
+ }
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
}
- clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags);
- /* Waiting for our command in done_queue to be returned to OS.*/
- if (qla2x00_eh_wait_for_pending_commands(pha, 0, 0, WAIT_HOST) ==
- QLA_SUCCESS)
+ /* Waiting for command to be returned to OS.*/
+ if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, sp, WAIT_HOST) ==
+ QLA_SUCCESS)
ret = SUCCESS;
- if (ha->parent)
- qla2x00_vp_abort_isp(ha);
-
eh_host_reset_lock:
qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__,
(ret == FAILED) ? "failed" : "succeded");
@@ -1021,35 +1119,36 @@ eh_host_reset_lock:
* 0 = success
*/
int
-qla2x00_loop_reset(scsi_qla_host_t *ha)
+qla2x00_loop_reset(scsi_qla_host_t *vha)
{
int ret;
struct fc_port *fcport;
+ struct qla_hw_data *ha = vha->hw;
- if (ha->flags.enable_lip_full_login) {
- ret = qla2x00_full_login_lip(ha);
+ if (ha->flags.enable_lip_full_login && !vha->vp_idx) {
+ ret = qla2x00_full_login_lip(vha);
if (ret != QLA_SUCCESS) {
- DEBUG2_3(printk("%s(%ld): bus_reset failed: "
- "full_login_lip=%d.\n", __func__, ha->host_no,
+ DEBUG2_3(printk("%s(%ld): failed: "
+ "full_login_lip=%d.\n", __func__, vha->host_no,
ret));
}
- atomic_set(&ha->loop_state, LOOP_DOWN);
- atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
- qla2x00_mark_all_devices_lost(ha, 0);
- qla2x00_wait_for_loop_ready(ha);
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(vha, 0);
+ qla2x00_wait_for_loop_ready(vha);
}
- if (ha->flags.enable_lip_reset) {
- ret = qla2x00_lip_reset(ha);
+ if (ha->flags.enable_lip_reset && !vha->vp_idx) {
+ ret = qla2x00_lip_reset(vha);
if (ret != QLA_SUCCESS) {
- DEBUG2_3(printk("%s(%ld): bus_reset failed: "
- "lip_reset=%d.\n", __func__, ha->host_no, ret));
- }
- qla2x00_wait_for_loop_ready(ha);
+ DEBUG2_3(printk("%s(%ld): failed: "
+ "lip_reset=%d.\n", __func__, vha->host_no, ret));
+ } else
+ qla2x00_wait_for_loop_ready(vha);
}
if (ha->flags.enable_target_reset) {
- list_for_each_entry(fcport, &ha->fcports, list) {
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (fcport->port_type != FCT_TARGET)
continue;
@@ -1057,31 +1156,37 @@ qla2x00_loop_reset(scsi_qla_host_t *ha)
if (ret != QLA_SUCCESS) {
DEBUG2_3(printk("%s(%ld): bus_reset failed: "
"target_reset=%d d_id=%x.\n", __func__,
- ha->host_no, ret, fcport->d_id.b24));
+ vha->host_no, ret, fcport->d_id.b24));
}
}
}
-
/* Issue marker command only when we are going to start the I/O */
- ha->marker_needed = 1;
+ vha->marker_needed = 1;
return QLA_SUCCESS;
}
void
-qla2x00_abort_all_cmds(scsi_qla_host_t *ha, int res)
+qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
{
- int cnt;
+ int que, cnt;
unsigned long flags;
srb_t *sp;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
spin_lock_irqsave(&ha->hardware_lock, flags);
- for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
- sp = ha->outstanding_cmds[cnt];
- if (sp) {
- ha->outstanding_cmds[cnt] = NULL;
- sp->cmd->result = res;
- qla2x00_sp_compl(ha, sp);
+ for (que = 0; que < QLA_MAX_HOST_QUES; que++) {
+ req = ha->req_q_map[vha->req_ques[que]];
+ if (!req)
+ continue;
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = req->outstanding_cmds[cnt];
+ if (sp && sp->vha == vha) {
+ req->outstanding_cmds[cnt] = NULL;
+ sp->cmd->result = res;
+ qla2x00_sp_compl(ha, sp);
+ }
}
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -1103,13 +1208,15 @@ qla2xxx_slave_alloc(struct scsi_device *sdev)
static int
qla2xxx_slave_configure(struct scsi_device *sdev)
{
- scsi_qla_host_t *ha = shost_priv(sdev->host);
+ scsi_qla_host_t *vha = shost_priv(sdev->host);
+ struct qla_hw_data *ha = vha->hw;
struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
+ struct req_que *req = ha->req_q_map[0];
if (sdev->tagged_supported)
- scsi_activate_tcq(sdev, ha->max_q_depth);
+ scsi_activate_tcq(sdev, req->max_q_depth);
else
- scsi_deactivate_tcq(sdev, ha->max_q_depth);
+ scsi_deactivate_tcq(sdev, req->max_q_depth);
rport->dev_loss_tmo = ha->port_down_retry_count;
@@ -1152,8 +1259,9 @@ qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type)
* supported addressing method.
*/
static void
-qla2x00_config_dma_addressing(scsi_qla_host_t *ha)
+qla2x00_config_dma_addressing(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
/* Assume a 32bit DMA mask. */
ha->flags.enable_64bit_addressing = 0;
@@ -1174,7 +1282,7 @@ qla2x00_config_dma_addressing(scsi_qla_host_t *ha)
}
static void
-qla2x00_enable_intrs(scsi_qla_host_t *ha)
+qla2x00_enable_intrs(struct qla_hw_data *ha)
{
unsigned long flags = 0;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -1189,7 +1297,7 @@ qla2x00_enable_intrs(scsi_qla_host_t *ha)
}
static void
-qla2x00_disable_intrs(scsi_qla_host_t *ha)
+qla2x00_disable_intrs(struct qla_hw_data *ha)
{
unsigned long flags = 0;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -1203,7 +1311,7 @@ qla2x00_disable_intrs(scsi_qla_host_t *ha)
}
static void
-qla24xx_enable_intrs(scsi_qla_host_t *ha)
+qla24xx_enable_intrs(struct qla_hw_data *ha)
{
unsigned long flags = 0;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
@@ -1216,7 +1324,7 @@ qla24xx_enable_intrs(scsi_qla_host_t *ha)
}
static void
-qla24xx_disable_intrs(scsi_qla_host_t *ha)
+qla24xx_disable_intrs(struct qla_hw_data *ha)
{
unsigned long flags = 0;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
@@ -1260,6 +1368,10 @@ static struct isp_operations qla2100_isp_ops = {
.read_optrom = qla2x00_read_optrom_data,
.write_optrom = qla2x00_write_optrom_data,
.get_flash_version = qla2x00_get_flash_version,
+ .start_scsi = qla2x00_start_scsi,
+ .wrt_req_reg = NULL,
+ .wrt_rsp_reg = NULL,
+ .rd_req_reg = NULL,
};
static struct isp_operations qla2300_isp_ops = {
@@ -1294,6 +1406,10 @@ static struct isp_operations qla2300_isp_ops = {
.read_optrom = qla2x00_read_optrom_data,
.write_optrom = qla2x00_write_optrom_data,
.get_flash_version = qla2x00_get_flash_version,
+ .start_scsi = qla2x00_start_scsi,
+ .wrt_req_reg = NULL,
+ .wrt_rsp_reg = NULL,
+ .rd_req_reg = NULL,
};
static struct isp_operations qla24xx_isp_ops = {
@@ -1328,6 +1444,10 @@ static struct isp_operations qla24xx_isp_ops = {
.read_optrom = qla24xx_read_optrom_data,
.write_optrom = qla24xx_write_optrom_data,
.get_flash_version = qla24xx_get_flash_version,
+ .start_scsi = qla24xx_start_scsi,
+ .wrt_req_reg = qla24xx_wrt_req_reg,
+ .wrt_rsp_reg = qla24xx_wrt_rsp_reg,
+ .rd_req_reg = qla24xx_rd_req_reg,
};
static struct isp_operations qla25xx_isp_ops = {
@@ -1362,10 +1482,14 @@ static struct isp_operations qla25xx_isp_ops = {
.read_optrom = qla25xx_read_optrom_data,
.write_optrom = qla24xx_write_optrom_data,
.get_flash_version = qla24xx_get_flash_version,
+ .start_scsi = qla24xx_start_scsi,
+ .wrt_req_reg = qla24xx_wrt_req_reg,
+ .wrt_rsp_reg = qla24xx_wrt_rsp_reg,
+ .rd_req_reg = qla24xx_rd_req_reg,
};
static inline void
-qla2x00_set_isp_flags(scsi_qla_host_t *ha)
+qla2x00_set_isp_flags(struct qla_hw_data *ha)
{
ha->device_type = DT_EXTENDED_IDS;
switch (ha->pdev->device) {
@@ -1447,9 +1571,10 @@ qla2x00_set_isp_flags(scsi_qla_host_t *ha)
}
static int
-qla2x00_iospace_config(scsi_qla_host_t *ha)
+qla2x00_iospace_config(struct qla_hw_data *ha)
{
resource_size_t pio;
+ uint16_t msix;
if (pci_request_selected_regions(ha->pdev, ha->bars,
QLA2XXX_DRIVER_NAME)) {
@@ -1502,6 +1627,30 @@ skip_pio:
goto iospace_error_exit;
}
+ /* Determine queue resources */
+ ha->max_queues = 1;
+ if (ql2xmaxqueues <= 1 || !IS_QLA25XX(ha))
+ goto mqiobase_exit;
+ ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 3),
+ pci_resource_len(ha->pdev, 3));
+ if (ha->mqiobase) {
+ /* Read MSIX vector size of the board */
+ pci_read_config_word(ha->pdev, QLA_PCI_MSIX_CONTROL, &msix);
+ ha->msix_count = msix;
+ /* Max queues are bounded by available msix vectors */
+ /* queue 0 uses two msix vectors */
+ if (ha->msix_count - 1 < ql2xmaxqueues)
+ ha->max_queues = ha->msix_count - 1;
+ else if (ql2xmaxqueues > QLA_MQ_SIZE)
+ ha->max_queues = QLA_MQ_SIZE;
+ else
+ ha->max_queues = ql2xmaxqueues;
+ qla_printk(KERN_INFO, ha,
+ "MSI-X vector count: %d\n", msix);
+ }
+
+mqiobase_exit:
+ ha->msix_count = ha->max_queues + 1;
return (0);
iospace_error_exit:
@@ -1511,25 +1660,25 @@ iospace_error_exit:
static void
qla2xxx_scan_start(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
- set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
- set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
- set_bit(RSCN_UPDATE, &ha->dpc_flags);
- set_bit(NPIV_CONFIG_NEEDED, &ha->dpc_flags);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ set_bit(RSCN_UPDATE, &vha->dpc_flags);
+ set_bit(NPIV_CONFIG_NEEDED, &vha->dpc_flags);
}
static int
qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
- if (!ha->host)
+ if (!vha->host)
return 1;
- if (time > ha->loop_reset_delay * HZ)
+ if (time > vha->hw->loop_reset_delay * HZ)
return 1;
- return atomic_read(&ha->loop_state) == LOOP_READY;
+ return atomic_read(&vha->loop_state) == LOOP_READY;
}
/*
@@ -1540,11 +1689,15 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
int ret = -ENODEV;
struct Scsi_Host *host;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *base_vha = NULL;
+ struct qla_hw_data *ha;
char pci_info[30];
char fw_str[30];
struct scsi_host_template *sht;
- int bars, mem_only = 0;
+ int bars, max_id, mem_only = 0;
+ uint16_t req_length = 0, rsp_length = 0;
+ struct req_que *req = NULL;
+ struct rsp_que *rsp = NULL;
bars = pci_select_bars(pdev, IORESOURCE_MEM | IORESOURCE_IO);
sht = &qla2x00_driver_template;
@@ -1570,33 +1723,24 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
/* This may fail but that's ok */
pci_enable_pcie_error_reporting(pdev);
- host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t));
- if (host == NULL) {
- printk(KERN_WARNING
- "qla2xxx: Couldn't allocate host from scsi layer!\n");
- goto probe_disable_device;
+ ha = kzalloc(sizeof(struct qla_hw_data), GFP_KERNEL);
+ if (!ha) {
+ DEBUG(printk("Unable to allocate memory for ha\n"));
+ goto probe_out;
}
+ ha->pdev = pdev;
/* Clear our data area */
- ha = shost_priv(host);
- memset(ha, 0, sizeof(scsi_qla_host_t));
-
- ha->pdev = pdev;
- ha->host = host;
- ha->host_no = host->host_no;
- sprintf(ha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, ha->host_no);
- ha->parent = NULL;
ha->bars = bars;
ha->mem_only = mem_only;
spin_lock_init(&ha->hardware_lock);
/* Set ISP-type information. */
qla2x00_set_isp_flags(ha);
-
/* Configure PCI I/O space */
ret = qla2x00_iospace_config(ha);
if (ret)
- goto probe_failed;
+ goto probe_hw_failed;
qla_printk(KERN_INFO, ha,
"Found an ISP%04X, irq %d, iobase 0x%p\n", pdev->device, pdev->irq,
@@ -1604,95 +1748,137 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->prev_topology = 0;
ha->init_cb_size = sizeof(init_cb_t);
- ha->mgmt_svr_loop_id = MANAGEMENT_SERVER + ha->vp_idx;
ha->link_data_rate = PORT_SPEED_UNKNOWN;
ha->optrom_size = OPTROM_SIZE_2300;
- ha->max_q_depth = MAX_Q_DEPTH;
- if (ql2xmaxqdepth != 0 && ql2xmaxqdepth <= 0xffffU)
- ha->max_q_depth = ql2xmaxqdepth;
-
/* Assign ISP specific operations. */
+ max_id = MAX_TARGETS_2200;
if (IS_QLA2100(ha)) {
- host->max_id = MAX_TARGETS_2100;
+ max_id = MAX_TARGETS_2100;
ha->mbx_count = MAILBOX_REGISTER_COUNT_2100;
- ha->request_q_length = REQUEST_ENTRY_CNT_2100;
- ha->response_q_length = RESPONSE_ENTRY_CNT_2100;
- ha->last_loop_id = SNS_LAST_LOOP_ID_2100;
- host->sg_tablesize = 32;
+ req_length = REQUEST_ENTRY_CNT_2100;
+ rsp_length = RESPONSE_ENTRY_CNT_2100;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2100;
ha->gid_list_info_size = 4;
ha->isp_ops = &qla2100_isp_ops;
} else if (IS_QLA2200(ha)) {
- host->max_id = MAX_TARGETS_2200;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
- ha->request_q_length = REQUEST_ENTRY_CNT_2200;
- ha->response_q_length = RESPONSE_ENTRY_CNT_2100;
- ha->last_loop_id = SNS_LAST_LOOP_ID_2100;
+ req_length = REQUEST_ENTRY_CNT_2200;
+ rsp_length = RESPONSE_ENTRY_CNT_2100;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2100;
ha->gid_list_info_size = 4;
ha->isp_ops = &qla2100_isp_ops;
} else if (IS_QLA23XX(ha)) {
- host->max_id = MAX_TARGETS_2200;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
- ha->request_q_length = REQUEST_ENTRY_CNT_2200;
- ha->response_q_length = RESPONSE_ENTRY_CNT_2300;
- ha->last_loop_id = SNS_LAST_LOOP_ID_2300;
+ req_length = REQUEST_ENTRY_CNT_2200;
+ rsp_length = RESPONSE_ENTRY_CNT_2300;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
ha->gid_list_info_size = 6;
if (IS_QLA2322(ha) || IS_QLA6322(ha))
ha->optrom_size = OPTROM_SIZE_2322;
ha->isp_ops = &qla2300_isp_ops;
} else if (IS_QLA24XX_TYPE(ha)) {
- host->max_id = MAX_TARGETS_2200;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
- ha->request_q_length = REQUEST_ENTRY_CNT_24XX;
- ha->response_q_length = RESPONSE_ENTRY_CNT_2300;
- ha->last_loop_id = SNS_LAST_LOOP_ID_2300;
+ req_length = REQUEST_ENTRY_CNT_24XX;
+ rsp_length = RESPONSE_ENTRY_CNT_2300;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
- ha->mgmt_svr_loop_id = 10 + ha->vp_idx;
ha->gid_list_info_size = 8;
ha->optrom_size = OPTROM_SIZE_24XX;
+ ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA24XX;
ha->isp_ops = &qla24xx_isp_ops;
} else if (IS_QLA25XX(ha)) {
- host->max_id = MAX_TARGETS_2200;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
- ha->request_q_length = REQUEST_ENTRY_CNT_24XX;
- ha->response_q_length = RESPONSE_ENTRY_CNT_2300;
- ha->last_loop_id = SNS_LAST_LOOP_ID_2300;
+ req_length = REQUEST_ENTRY_CNT_24XX;
+ rsp_length = RESPONSE_ENTRY_CNT_2300;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
- ha->mgmt_svr_loop_id = 10 + ha->vp_idx;
ha->gid_list_info_size = 8;
ha->optrom_size = OPTROM_SIZE_25XX;
+ ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
ha->isp_ops = &qla25xx_isp_ops;
}
- host->can_queue = ha->request_q_length + 128;
mutex_init(&ha->vport_lock);
init_completion(&ha->mbx_cmd_comp);
complete(&ha->mbx_cmd_comp);
init_completion(&ha->mbx_intr_comp);
- INIT_LIST_HEAD(&ha->list);
- INIT_LIST_HEAD(&ha->fcports);
- INIT_LIST_HEAD(&ha->vp_list);
- INIT_LIST_HEAD(&ha->work_list);
-
set_bit(0, (unsigned long *) ha->vp_idx_map);
- qla2x00_config_dma_addressing(ha);
- if (qla2x00_mem_alloc(ha)) {
+ ret = qla2x00_mem_alloc(ha, req_length, rsp_length, &req, &rsp);
+ if (!ret) {
qla_printk(KERN_WARNING, ha,
"[ERROR] Failed to allocate memory for adapter\n");
+ goto probe_hw_failed;
+ }
+
+ req->max_q_depth = MAX_Q_DEPTH;
+ if (ql2xmaxqdepth != 0 && ql2xmaxqdepth <= 0xffffU)
+ req->max_q_depth = ql2xmaxqdepth;
+
+
+ base_vha = qla2x00_create_host(sht, ha);
+ if (!base_vha) {
+ qla_printk(KERN_WARNING, ha,
+ "[ERROR] Failed to allocate memory for scsi_host\n");
+
ret = -ENOMEM;
+ goto probe_hw_failed;
+ }
+
+ pci_set_drvdata(pdev, base_vha);
+
+ qla2x00_config_dma_addressing(base_vha);
+
+ host = base_vha->host;
+ base_vha->req_ques[0] = req->id;
+ host->can_queue = req->length + 128;
+ if (IS_QLA2XXX_MIDTYPE(ha))
+ base_vha->mgmt_svr_loop_id = 10 + base_vha->vp_idx;
+ else
+ base_vha->mgmt_svr_loop_id = MANAGEMENT_SERVER +
+ base_vha->vp_idx;
+ if (IS_QLA2100(ha))
+ host->sg_tablesize = 32;
+ host->max_id = max_id;
+ host->this_id = 255;
+ host->cmd_per_lun = 3;
+ host->unique_id = host->host_no;
+ host->max_cmd_len = MAX_CMDSZ;
+ host->max_channel = MAX_BUSES - 1;
+ host->max_lun = MAX_LUNS;
+ host->transportt = qla2xxx_transport_template;
+
+ /* Set up the irqs */
+ ret = qla2x00_request_irqs(ha, rsp);
+ if (ret)
+ goto probe_failed;
+
+ /* Alloc arrays of request and response ring ptrs */
+ if (!qla2x00_alloc_queues(ha)) {
+ qla_printk(KERN_WARNING, ha,
+ "[ERROR] Failed to allocate memory for queue"
+ " pointers\n");
goto probe_failed;
}
+ ha->rsp_q_map[0] = rsp;
+ ha->req_q_map[0] = req;
- if (qla2x00_initialize_adapter(ha)) {
+ if (ha->mqenable) {
+ ha->isp_ops->wrt_req_reg = qla25xx_wrt_req_reg;
+ ha->isp_ops->wrt_rsp_reg = qla25xx_wrt_rsp_reg;
+ ha->isp_ops->rd_req_reg = qla25xx_rd_req_reg;
+ }
+
+ if (qla2x00_initialize_adapter(base_vha)) {
qla_printk(KERN_WARNING, ha,
"Failed to initialize adapter\n");
DEBUG2(printk("scsi(%ld): Failed to initialize adapter - "
"Adapter flags %x.\n",
- ha->host_no, ha->device_flags));
+ base_vha->host_no, base_vha->device_flags));
ret = -ENODEV;
goto probe_failed;
@@ -1702,7 +1888,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
* Startup the kernel thread for this host adapter
*/
ha->dpc_thread = kthread_create(qla2x00_do_dpc, ha,
- "%s_dpc", ha->host_str);
+ "%s_dpc", base_vha->host_str);
if (IS_ERR(ha->dpc_thread)) {
qla_printk(KERN_WARNING, ha,
"Unable to start DPC thread!\n");
@@ -1710,28 +1896,17 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
goto probe_failed;
}
- host->this_id = 255;
- host->cmd_per_lun = 3;
- host->unique_id = host->host_no;
- host->max_cmd_len = MAX_CMDSZ;
- host->max_channel = MAX_BUSES - 1;
- host->max_lun = MAX_LUNS;
- host->transportt = qla2xxx_transport_template;
-
- ret = qla2x00_request_irqs(ha);
- if (ret)
- goto probe_failed;
+ list_add_tail(&base_vha->list, &ha->vp_list);
+ base_vha->host->irq = ha->pdev->irq;
/* Initialized the timer */
- qla2x00_start_timer(ha, qla2x00_timer, WATCH_INTERVAL);
+ qla2x00_start_timer(base_vha, qla2x00_timer, WATCH_INTERVAL);
DEBUG2(printk("DEBUG: detect hba %ld at address = %p\n",
- ha->host_no, ha));
+ base_vha->host_no, ha));
- pci_set_drvdata(pdev, ha);
-
- ha->flags.init_done = 1;
- ha->flags.online = 1;
+ base_vha->flags.init_done = 1;
+ base_vha->flags.online = 1;
ret = scsi_add_host(host, &pdev->dev);
if (ret)
@@ -1741,76 +1916,98 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
scsi_scan_host(host);
- qla2x00_alloc_sysfs_attr(ha);
+ qla2x00_alloc_sysfs_attr(base_vha);
- qla2x00_init_host_attr(ha);
+ qla2x00_init_host_attr(base_vha);
- qla2x00_dfs_setup(ha);
+ qla2x00_dfs_setup(base_vha);
qla_printk(KERN_INFO, ha, "\n"
" QLogic Fibre Channel HBA Driver: %s\n"
" QLogic %s - %s\n"
" ISP%04X: %s @ %s hdma%c, host#=%ld, fw=%s\n",
qla2x00_version_str, ha->model_number,
- ha->model_desc ? ha->model_desc: "", pdev->device,
- ha->isp_ops->pci_info_str(ha, pci_info), pci_name(pdev),
- ha->flags.enable_64bit_addressing ? '+': '-', ha->host_no,
- ha->isp_ops->fw_version_str(ha, fw_str));
+ ha->model_desc ? ha->model_desc : "", pdev->device,
+ ha->isp_ops->pci_info_str(base_vha, pci_info), pci_name(pdev),
+ ha->flags.enable_64bit_addressing ? '+' : '-', base_vha->host_no,
+ ha->isp_ops->fw_version_str(base_vha, fw_str));
return 0;
probe_failed:
- qla2x00_free_device(ha);
+ qla2x00_free_que(ha, req, rsp);
+ qla2x00_free_device(base_vha);
- scsi_host_put(host);
+ scsi_host_put(base_vha->host);
-probe_disable_device:
- pci_disable_device(pdev);
+probe_hw_failed:
+ if (ha->iobase)
+ iounmap(ha->iobase);
+
+ pci_release_selected_regions(ha->pdev, ha->bars);
+ kfree(ha);
+ ha = NULL;
probe_out:
+ pci_disable_device(pdev);
return ret;
}
static void
qla2x00_remove_one(struct pci_dev *pdev)
{
- scsi_qla_host_t *ha, *vha, *temp;
+ scsi_qla_host_t *base_vha, *vha, *temp;
+ struct qla_hw_data *ha;
+
+ base_vha = pci_get_drvdata(pdev);
+ ha = base_vha->hw;
+
+ list_for_each_entry_safe(vha, temp, &ha->vp_list, list) {
+ if (vha && vha->fc_vport)
+ fc_vport_terminate(vha->fc_vport);
+ }
- ha = pci_get_drvdata(pdev);
+ set_bit(UNLOADING, &base_vha->dpc_flags);
- list_for_each_entry_safe(vha, temp, &ha->vp_list, vp_list)
- fc_vport_terminate(vha->fc_vport);
+ qla2x00_dfs_remove(base_vha);
- set_bit(UNLOADING, &ha->dpc_flags);
+ qla84xx_put_chip(base_vha);
- qla2x00_dfs_remove(ha);
+ qla2x00_free_sysfs_attr(base_vha);
- qla84xx_put_chip(ha);
+ fc_remove_host(base_vha->host);
- qla2x00_free_sysfs_attr(ha);
+ scsi_remove_host(base_vha->host);
- fc_remove_host(ha->host);
+ qla2x00_free_device(base_vha);
- scsi_remove_host(ha->host);
+ scsi_host_put(base_vha->host);
- qla2x00_free_device(ha);
+ if (ha->iobase)
+ iounmap(ha->iobase);
- scsi_host_put(ha->host);
+ if (ha->mqiobase)
+ iounmap(ha->mqiobase);
+
+ pci_release_selected_regions(ha->pdev, ha->bars);
+ kfree(ha);
+ ha = NULL;
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
}
static void
-qla2x00_free_device(scsi_qla_host_t *ha)
+qla2x00_free_device(scsi_qla_host_t *vha)
{
- qla2x00_abort_all_cmds(ha, DID_NO_CONNECT << 16);
+ struct qla_hw_data *ha = vha->hw;
+ qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16);
/* Disable timer */
- if (ha->timer_active)
- qla2x00_stop_timer(ha);
+ if (vha->timer_active)
+ qla2x00_stop_timer(vha);
- ha->flags.online = 0;
+ vha->flags.online = 0;
/* Kill the kernel thread for this host */
if (ha->dpc_thread) {
@@ -1825,45 +2022,41 @@ qla2x00_free_device(scsi_qla_host_t *ha)
}
if (ha->flags.fce_enabled)
- qla2x00_disable_fce_trace(ha, NULL, NULL);
+ qla2x00_disable_fce_trace(vha, NULL, NULL);
if (ha->eft)
- qla2x00_disable_eft_trace(ha);
+ qla2x00_disable_eft_trace(vha);
/* Stop currently executing firmware. */
- qla2x00_try_to_stop_firmware(ha);
+ qla2x00_try_to_stop_firmware(vha);
/* turn-off interrupts on the card */
if (ha->interrupts_on)
ha->isp_ops->disable_intrs(ha);
- qla2x00_mem_free(ha);
+ qla2x00_free_irqs(vha);
- qla2x00_free_irqs(ha);
+ qla2x00_mem_free(ha);
- /* release io space registers */
- if (ha->iobase)
- iounmap(ha->iobase);
- pci_release_selected_regions(ha->pdev, ha->bars);
+ qla2x00_free_queues(ha);
}
static inline void
-qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport,
+qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport,
int defer)
{
struct fc_rport *rport;
- scsi_qla_host_t *pha = to_qla_parent(ha);
if (!fcport->rport)
return;
rport = fcport->rport;
if (defer) {
- spin_lock_irq(ha->host->host_lock);
+ spin_lock_irq(vha->host->host_lock);
fcport->drport = rport;
- spin_unlock_irq(ha->host->host_lock);
- set_bit(FCPORT_UPDATE_NEEDED, &pha->dpc_flags);
- qla2xxx_wake_dpc(pha);
+ spin_unlock_irq(vha->host->host_lock);
+ set_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
} else
fc_remote_port_delete(rport);
}
@@ -1877,13 +2070,14 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport,
*
* Context:
*/
-void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport,
+void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport,
int do_login, int defer)
{
if (atomic_read(&fcport->state) == FCS_ONLINE &&
- ha->vp_idx == fcport->vp_idx)
- qla2x00_schedule_rport_del(ha, fcport, defer);
-
+ vha->vp_idx == fcport->vp_idx) {
+ atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ qla2x00_schedule_rport_del(vha, fcport, defer);
+ }
/*
* We may need to retry the login, so don't change the state of the
* port but do the retries.
@@ -1895,13 +2089,13 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport,
return;
if (fcport->login_retry == 0) {
- fcport->login_retry = ha->login_retry_count;
- set_bit(RELOGIN_NEEDED, &ha->dpc_flags);
+ fcport->login_retry = vha->hw->login_retry_count;
+ set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
DEBUG(printk("scsi(%ld): Port login retry: "
"%02x%02x%02x%02x%02x%02x%02x%02x, "
"id = 0x%04x retry cnt=%d\n",
- ha->host_no,
+ vha->host_no,
fcport->port_name[0],
fcport->port_name[1],
fcport->port_name[2],
@@ -1929,13 +2123,12 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport,
* Context:
*/
void
-qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
+qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
{
fc_port_t *fcport;
- scsi_qla_host_t *pha = to_qla_parent(ha);
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (ha->vp_idx != fcport->vp_idx)
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (vha->vp_idx != fcport->vp_idx)
continue;
/*
* No point in marking the device as lost, if the device is
@@ -1943,9 +2136,11 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
*/
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
continue;
- if (atomic_read(&fcport->state) == FCS_ONLINE)
- qla2x00_schedule_rport_del(ha, fcport, defer);
- atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ if (atomic_read(&fcport->state) == FCS_ONLINE) {
+ atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ qla2x00_schedule_rport_del(vha, fcport, defer);
+ } else
+ atomic_set(&fcport->state, FCS_DEVICE_LOST);
}
}
@@ -1958,105 +2153,153 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
* !0 = failure.
*/
static int
-qla2x00_mem_alloc(scsi_qla_host_t *ha)
+qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
+ struct req_que **req, struct rsp_que **rsp)
{
char name[16];
- ha->request_ring = dma_alloc_coherent(&ha->pdev->dev,
- (ha->request_q_length + 1) * sizeof(request_t), &ha->request_dma,
- GFP_KERNEL);
- if (!ha->request_ring)
- goto fail;
-
- ha->response_ring = dma_alloc_coherent(&ha->pdev->dev,
- (ha->response_q_length + 1) * sizeof(response_t),
- &ha->response_dma, GFP_KERNEL);
- if (!ha->response_ring)
- goto fail_free_request_ring;
-
- ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE,
- &ha->gid_list_dma, GFP_KERNEL);
- if (!ha->gid_list)
- goto fail_free_response_ring;
+ ha->init_cb_size = sizeof(init_cb_t);
+ if (IS_QLA2XXX_MIDTYPE(ha))
+ ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
ha->init_cb = dma_alloc_coherent(&ha->pdev->dev, ha->init_cb_size,
- &ha->init_cb_dma, GFP_KERNEL);
+ &ha->init_cb_dma, GFP_KERNEL);
if (!ha->init_cb)
- goto fail_free_gid_list;
+ goto fail;
- snprintf(name, sizeof(name), "%s_%ld", QLA2XXX_DRIVER_NAME,
- ha->host_no);
- ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
- DMA_POOL_SIZE, 8, 0);
- if (!ha->s_dma_pool)
+ ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE,
+ &ha->gid_list_dma, GFP_KERNEL);
+ if (!ha->gid_list)
goto fail_free_init_cb;
ha->srb_mempool = mempool_create_slab_pool(SRB_MIN_REQ, srb_cachep);
if (!ha->srb_mempool)
- goto fail_free_s_dma_pool;
+ goto fail_free_gid_list;
/* Get memory for cached NVRAM */
ha->nvram = kzalloc(MAX_NVRAM_SIZE, GFP_KERNEL);
if (!ha->nvram)
goto fail_free_srb_mempool;
+ snprintf(name, sizeof(name), "%s_%d", QLA2XXX_DRIVER_NAME,
+ ha->pdev->device);
+ ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
+ DMA_POOL_SIZE, 8, 0);
+ if (!ha->s_dma_pool)
+ goto fail_free_nvram;
+
/* Allocate memory for SNS commands */
if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- /* Get consistent memory allocated for SNS commands */
+ /* Get consistent memory allocated for SNS commands */
ha->sns_cmd = dma_alloc_coherent(&ha->pdev->dev,
- sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma, GFP_KERNEL);
+ sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma, GFP_KERNEL);
if (!ha->sns_cmd)
- goto fail_free_nvram;
+ goto fail_dma_pool;
} else {
- /* Get consistent memory allocated for MS IOCB */
+ /* Get consistent memory allocated for MS IOCB */
ha->ms_iocb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
- &ha->ms_iocb_dma);
+ &ha->ms_iocb_dma);
if (!ha->ms_iocb)
- goto fail_free_nvram;
-
- /* Get consistent memory allocated for CT SNS commands */
+ goto fail_dma_pool;
+ /* Get consistent memory allocated for CT SNS commands */
ha->ct_sns = dma_alloc_coherent(&ha->pdev->dev,
- sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL);
+ sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL);
if (!ha->ct_sns)
goto fail_free_ms_iocb;
}
- return 0;
+ /* Allocate memory for request ring */
+ *req = kzalloc(sizeof(struct req_que), GFP_KERNEL);
+ if (!*req) {
+ DEBUG(printk("Unable to allocate memory for req\n"));
+ goto fail_req;
+ }
+ (*req)->length = req_len;
+ (*req)->ring = dma_alloc_coherent(&ha->pdev->dev,
+ ((*req)->length + 1) * sizeof(request_t),
+ &(*req)->dma, GFP_KERNEL);
+ if (!(*req)->ring) {
+ DEBUG(printk("Unable to allocate memory for req_ring\n"));
+ goto fail_req_ring;
+ }
+ /* Allocate memory for response ring */
+ *rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL);
+ if (!*rsp) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for rsp\n");
+ goto fail_rsp;
+ }
+ (*rsp)->hw = ha;
+ (*rsp)->length = rsp_len;
+ (*rsp)->ring = dma_alloc_coherent(&ha->pdev->dev,
+ ((*rsp)->length + 1) * sizeof(response_t),
+ &(*rsp)->dma, GFP_KERNEL);
+ if (!(*rsp)->ring) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for rsp_ring\n");
+ goto fail_rsp_ring;
+ }
+ (*req)->rsp = *rsp;
+ (*rsp)->req = *req;
+ /* Allocate memory for NVRAM data for vports */
+ if (ha->nvram_npiv_size) {
+ ha->npiv_info = kzalloc(sizeof(struct qla_npiv_entry) *
+ ha->nvram_npiv_size, GFP_KERNEL);
+ if (!ha->npiv_info) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for npiv info\n");
+ goto fail_npiv_info;
+ }
+ } else
+ ha->npiv_info = NULL;
+ INIT_LIST_HEAD(&ha->vp_list);
+ return 1;
+
+fail_npiv_info:
+ dma_free_coherent(&ha->pdev->dev, ((*rsp)->length + 1) *
+ sizeof(response_t), (*rsp)->ring, (*rsp)->dma);
+ (*rsp)->ring = NULL;
+ (*rsp)->dma = 0;
+fail_rsp_ring:
+ kfree(*rsp);
+fail_rsp:
+ dma_free_coherent(&ha->pdev->dev, ((*req)->length + 1) *
+ sizeof(request_t), (*req)->ring, (*req)->dma);
+ (*req)->ring = NULL;
+ (*req)->dma = 0;
+fail_req_ring:
+ kfree(*req);
+fail_req:
+ dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
+ ha->ct_sns, ha->ct_sns_dma);
+ ha->ct_sns = NULL;
+ ha->ct_sns_dma = 0;
fail_free_ms_iocb:
dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
ha->ms_iocb = NULL;
ha->ms_iocb_dma = 0;
+fail_dma_pool:
+ dma_pool_destroy(ha->s_dma_pool);
+ ha->s_dma_pool = NULL;
fail_free_nvram:
kfree(ha->nvram);
ha->nvram = NULL;
fail_free_srb_mempool:
mempool_destroy(ha->srb_mempool);
ha->srb_mempool = NULL;
-fail_free_s_dma_pool:
- dma_pool_destroy(ha->s_dma_pool);
- ha->s_dma_pool = NULL;
-fail_free_init_cb:
- dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb,
- ha->init_cb_dma);
- ha->init_cb = NULL;
- ha->init_cb_dma = 0;
fail_free_gid_list:
dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list,
- ha->gid_list_dma);
+ ha->gid_list_dma);
ha->gid_list = NULL;
ha->gid_list_dma = 0;
-fail_free_response_ring:
- dma_free_coherent(&ha->pdev->dev, (ha->response_q_length + 1) *
- sizeof(response_t), ha->response_ring, ha->response_dma);
- ha->response_ring = NULL;
- ha->response_dma = 0;
-fail_free_request_ring:
- dma_free_coherent(&ha->pdev->dev, (ha->request_q_length + 1) *
- sizeof(request_t), ha->request_ring, ha->request_dma);
- ha->request_ring = NULL;
- ha->request_dma = 0;
+fail_free_init_cb:
+ dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb,
+ ha->init_cb_dma);
+ ha->init_cb = NULL;
+ ha->init_cb_dma = 0;
fail:
+ DEBUG(printk("%s: Memory allocation failure\n", __func__));
return -ENOMEM;
}
@@ -2068,32 +2311,29 @@ fail:
* ha = adapter block pointer.
*/
static void
-qla2x00_mem_free(scsi_qla_host_t *ha)
+qla2x00_mem_free(struct qla_hw_data *ha)
{
- struct list_head *fcpl, *fcptemp;
- fc_port_t *fcport;
-
if (ha->srb_mempool)
mempool_destroy(ha->srb_mempool);
if (ha->fce)
dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce,
- ha->fce_dma);
+ ha->fce_dma);
if (ha->fw_dump) {
if (ha->eft)
dma_free_coherent(&ha->pdev->dev,
- ntohl(ha->fw_dump->eft_size), ha->eft, ha->eft_dma);
+ ntohl(ha->fw_dump->eft_size), ha->eft, ha->eft_dma);
vfree(ha->fw_dump);
}
if (ha->sns_cmd)
dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
- ha->sns_cmd, ha->sns_cmd_dma);
+ ha->sns_cmd, ha->sns_cmd_dma);
if (ha->ct_sns)
dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
- ha->ct_sns, ha->ct_sns_dma);
+ ha->ct_sns, ha->ct_sns_dma);
if (ha->sfp_data)
dma_pool_free(ha->s_dma_pool, ha->sfp_data, ha->sfp_data_dma);
@@ -2104,23 +2344,18 @@ qla2x00_mem_free(scsi_qla_host_t *ha)
if (ha->s_dma_pool)
dma_pool_destroy(ha->s_dma_pool);
- if (ha->init_cb)
- dma_free_coherent(&ha->pdev->dev, ha->init_cb_size,
- ha->init_cb, ha->init_cb_dma);
if (ha->gid_list)
dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list,
- ha->gid_list_dma);
+ ha->gid_list_dma);
- if (ha->response_ring)
- dma_free_coherent(&ha->pdev->dev,
- (ha->response_q_length + 1) * sizeof(response_t),
- ha->response_ring, ha->response_dma);
- if (ha->request_ring)
- dma_free_coherent(&ha->pdev->dev,
- (ha->request_q_length + 1) * sizeof(request_t),
- ha->request_ring, ha->request_dma);
+ if (ha->init_cb)
+ dma_free_coherent(&ha->pdev->dev, ha->init_cb_size,
+ ha->init_cb, ha->init_cb_dma);
+ vfree(ha->optrom_buffer);
+ kfree(ha->nvram);
+ kfree(ha->npiv_info);
ha->srb_mempool = NULL;
ha->eft = NULL;
@@ -2139,30 +2374,45 @@ qla2x00_mem_free(scsi_qla_host_t *ha)
ha->gid_list = NULL;
ha->gid_list_dma = 0;
- ha->response_ring = NULL;
- ha->response_dma = 0;
- ha->request_ring = NULL;
- ha->request_dma = 0;
+ ha->fw_dump = NULL;
+ ha->fw_dumped = 0;
+ ha->fw_dump_reading = 0;
+}
- list_for_each_safe(fcpl, fcptemp, &ha->fcports) {
- fcport = list_entry(fcpl, fc_port_t, list);
+struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
+ struct qla_hw_data *ha)
+{
+ struct Scsi_Host *host;
+ struct scsi_qla_host *vha = NULL;
- /* fc ports */
- list_del_init(&fcport->list);
- kfree(fcport);
+ host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t));
+ if (host == NULL) {
+ printk(KERN_WARNING
+ "qla2xxx: Couldn't allocate host from scsi layer!\n");
+ goto fail;
}
- INIT_LIST_HEAD(&ha->fcports);
- ha->fw_dump = NULL;
- ha->fw_dumped = 0;
- ha->fw_dump_reading = 0;
+ /* Clear our data area */
+ vha = shost_priv(host);
+ memset(vha, 0, sizeof(scsi_qla_host_t));
- vfree(ha->optrom_buffer);
- kfree(ha->nvram);
+ vha->host = host;
+ vha->host_no = host->host_no;
+ vha->hw = ha;
+
+ INIT_LIST_HEAD(&vha->vp_fcports);
+ INIT_LIST_HEAD(&vha->work_list);
+ INIT_LIST_HEAD(&vha->list);
+
+ sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no);
+ return vha;
+
+fail:
+ return vha;
}
static struct qla_work_evt *
-qla2x00_alloc_work(struct scsi_qla_host *ha, enum qla_work_type type,
+qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type,
int locked)
{
struct qla_work_evt *e;
@@ -2179,42 +2429,42 @@ qla2x00_alloc_work(struct scsi_qla_host *ha, enum qla_work_type type,
}
static int
-qla2x00_post_work(struct scsi_qla_host *ha, struct qla_work_evt *e, int locked)
+qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e, int locked)
{
unsigned long uninitialized_var(flags);
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
if (!locked)
- spin_lock_irqsave(&pha->hardware_lock, flags);
- list_add_tail(&e->list, &ha->work_list);
- qla2xxx_wake_dpc(ha);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ list_add_tail(&e->list, &vha->work_list);
+ qla2xxx_wake_dpc(vha);
if (!locked)
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
return QLA_SUCCESS;
}
int
-qla2x00_post_aen_work(struct scsi_qla_host *ha, enum fc_host_event_code code,
+qla2x00_post_aen_work(struct scsi_qla_host *vha, enum fc_host_event_code code,
u32 data)
{
struct qla_work_evt *e;
- e = qla2x00_alloc_work(ha, QLA_EVT_AEN, 1);
+ e = qla2x00_alloc_work(vha, QLA_EVT_AEN, 1);
if (!e)
return QLA_FUNCTION_FAILED;
e->u.aen.code = code;
e->u.aen.data = data;
- return qla2x00_post_work(ha, e, 1);
+ return qla2x00_post_work(vha, e, 1);
}
int
-qla2x00_post_hwe_work(struct scsi_qla_host *ha, uint16_t code, uint16_t d1,
+qla2x00_post_hwe_work(struct scsi_qla_host *vha, uint16_t code, uint16_t d1,
uint16_t d2, uint16_t d3)
{
struct qla_work_evt *e;
- e = qla2x00_alloc_work(ha, QLA_EVT_HWE_LOG, 1);
+ e = qla2x00_alloc_work(vha, QLA_EVT_HWE_LOG, 1);
if (!e)
return QLA_FUNCTION_FAILED;
@@ -2222,36 +2472,95 @@ qla2x00_post_hwe_work(struct scsi_qla_host *ha, uint16_t code, uint16_t d1,
e->u.hwe.d1 = d1;
e->u.hwe.d2 = d2;
e->u.hwe.d3 = d3;
- return qla2x00_post_work(ha, e, 1);
+ return qla2x00_post_work(vha, e, 1);
}
static void
-qla2x00_do_work(struct scsi_qla_host *ha)
+qla2x00_do_work(struct scsi_qla_host *vha)
{
struct qla_work_evt *e;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
- spin_lock_irq(&pha->hardware_lock);
- while (!list_empty(&ha->work_list)) {
- e = list_entry(ha->work_list.next, struct qla_work_evt, list);
+ spin_lock_irq(&ha->hardware_lock);
+ while (!list_empty(&vha->work_list)) {
+ e = list_entry(vha->work_list.next, struct qla_work_evt, list);
list_del_init(&e->list);
- spin_unlock_irq(&pha->hardware_lock);
+ spin_unlock_irq(&ha->hardware_lock);
switch (e->type) {
case QLA_EVT_AEN:
- fc_host_post_event(ha->host, fc_get_event_number(),
+ fc_host_post_event(vha->host, fc_get_event_number(),
e->u.aen.code, e->u.aen.data);
break;
case QLA_EVT_HWE_LOG:
- qla2xxx_hw_event_log(ha, e->u.hwe.code, e->u.hwe.d1,
+ qla2xxx_hw_event_log(vha, e->u.hwe.code, e->u.hwe.d1,
e->u.hwe.d2, e->u.hwe.d3);
break;
}
if (e->flags & QLA_EVT_FLAG_FREE)
kfree(e);
- spin_lock_irq(&pha->hardware_lock);
+ spin_lock_irq(&ha->hardware_lock);
+ }
+ spin_unlock_irq(&ha->hardware_lock);
+}
+/* Relogins all the fcports of a vport
+ * Context: dpc thread
+ */
+void qla2x00_relogin(struct scsi_qla_host *vha)
+{
+ fc_port_t *fcport;
+ uint8_t status;
+ uint16_t next_loopid = 0;
+ struct qla_hw_data *ha = vha->hw;
+
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ /*
+ * If the port is not ONLINE then try to login
+ * to it if we haven't run out of retries.
+ */
+ if (atomic_read(&fcport->state) !=
+ FCS_ONLINE && fcport->login_retry) {
+
+ if (fcport->flags & FCF_FABRIC_DEVICE) {
+ if (fcport->flags & FCF_TAPE_PRESENT)
+ ha->isp_ops->fabric_logout(vha,
+ fcport->loop_id,
+ fcport->d_id.b.domain,
+ fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+
+ status = qla2x00_fabric_login(vha, fcport,
+ &next_loopid);
+ } else
+ status = qla2x00_local_device_login(vha,
+ fcport);
+
+ fcport->login_retry--;
+ if (status == QLA_SUCCESS) {
+ fcport->old_loop_id = fcport->loop_id;
+
+ DEBUG(printk("scsi(%ld): port login OK: logged "
+ "in ID 0x%x\n", vha->host_no, fcport->loop_id));
+
+ qla2x00_update_fcport(vha, fcport);
+
+ } else if (status == 1) {
+ set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+ /* retry the login again */
+ DEBUG(printk("scsi(%ld): Retrying"
+ " %d login again loop_id 0x%x\n",
+ vha->host_no, fcport->login_retry,
+ fcport->loop_id));
+ } else {
+ fcport->login_retry = 0;
+ }
+
+ if (fcport->login_retry == 0 && status != QLA_SUCCESS)
+ fcport->loop_id = FC_NO_LOOP_ID;
+ }
+ if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
+ break;
}
- spin_unlock_irq(&pha->hardware_lock);
}
/**************************************************************************
@@ -2271,15 +2580,11 @@ static int
qla2x00_do_dpc(void *data)
{
int rval;
- scsi_qla_host_t *ha;
- fc_port_t *fcport;
- uint8_t status;
- uint16_t next_loopid;
- struct scsi_qla_host *vha;
- int i;
+ scsi_qla_host_t *base_vha;
+ struct qla_hw_data *ha;
-
- ha = (scsi_qla_host_t *)data;
+ ha = (struct qla_hw_data *)data;
+ base_vha = pci_get_drvdata(ha->pdev);
set_user_nice(current, -20);
@@ -2293,10 +2598,10 @@ qla2x00_do_dpc(void *data)
DEBUG3(printk("qla2x00: DPC handler waking up\n"));
/* Initialization not yet finished. Don't do anything yet. */
- if (!ha->flags.init_done)
+ if (!base_vha->flags.init_done)
continue;
- DEBUG3(printk("scsi(%ld): DPC handler\n", ha->host_no));
+ DEBUG3(printk("scsi(%ld): DPC handler\n", base_vha->host_no));
ha->dpc_active = 1;
@@ -2305,149 +2610,98 @@ qla2x00_do_dpc(void *data)
continue;
}
- qla2x00_do_work(ha);
+ qla2x00_do_work(base_vha);
- if (test_and_clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) {
+ if (test_and_clear_bit(ISP_ABORT_NEEDED,
+ &base_vha->dpc_flags)) {
DEBUG(printk("scsi(%ld): dpc: sched "
"qla2x00_abort_isp ha = %p\n",
- ha->host_no, ha));
+ base_vha->host_no, ha));
if (!(test_and_set_bit(ABORT_ISP_ACTIVE,
- &ha->dpc_flags))) {
+ &base_vha->dpc_flags))) {
- if (qla2x00_abort_isp(ha)) {
+ if (qla2x00_abort_isp(base_vha)) {
/* failed. retry later */
set_bit(ISP_ABORT_NEEDED,
- &ha->dpc_flags);
- }
- clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
- }
-
- for_each_mapped_vp_idx(ha, i) {
- list_for_each_entry(vha, &ha->vp_list,
- vp_list) {
- if (i == vha->vp_idx) {
- set_bit(ISP_ABORT_NEEDED,
- &vha->dpc_flags);
- break;
- }
+ &base_vha->dpc_flags);
}
+ clear_bit(ABORT_ISP_ACTIVE,
+ &base_vha->dpc_flags);
}
DEBUG(printk("scsi(%ld): dpc: qla2x00_abort_isp end\n",
- ha->host_no));
+ base_vha->host_no));
}
- if (test_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags)) {
- qla2x00_update_fcports(ha);
- clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags);
+ if (test_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags)) {
+ qla2x00_update_fcports(base_vha);
+ clear_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
}
- if (test_and_clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) &&
- (!(test_and_set_bit(RESET_ACTIVE, &ha->dpc_flags)))) {
+ if (test_and_clear_bit(RESET_MARKER_NEEDED,
+ &base_vha->dpc_flags) &&
+ (!(test_and_set_bit(RESET_ACTIVE, &base_vha->dpc_flags)))) {
DEBUG(printk("scsi(%ld): qla2x00_reset_marker()\n",
- ha->host_no));
+ base_vha->host_no));
- qla2x00_rst_aen(ha);
- clear_bit(RESET_ACTIVE, &ha->dpc_flags);
+ qla2x00_rst_aen(base_vha);
+ clear_bit(RESET_ACTIVE, &base_vha->dpc_flags);
}
/* Retry each device up to login retry count */
- if ((test_and_clear_bit(RELOGIN_NEEDED, &ha->dpc_flags)) &&
- !test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) &&
- atomic_read(&ha->loop_state) != LOOP_DOWN) {
+ if ((test_and_clear_bit(RELOGIN_NEEDED,
+ &base_vha->dpc_flags)) &&
+ !test_bit(LOOP_RESYNC_NEEDED, &base_vha->dpc_flags) &&
+ atomic_read(&base_vha->loop_state) != LOOP_DOWN) {
DEBUG(printk("scsi(%ld): qla2x00_port_login()\n",
- ha->host_no));
-
- next_loopid = 0;
- list_for_each_entry(fcport, &ha->fcports, list) {
- /*
- * If the port is not ONLINE then try to login
- * to it if we haven't run out of retries.
- */
- if (atomic_read(&fcport->state) != FCS_ONLINE &&
- fcport->login_retry) {
-
- if (fcport->flags & FCF_FABRIC_DEVICE) {
- if (fcport->flags &
- FCF_TAPE_PRESENT)
- ha->isp_ops->fabric_logout(
- ha, fcport->loop_id,
- fcport->d_id.b.domain,
- fcport->d_id.b.area,
- fcport->d_id.b.al_pa);
- status = qla2x00_fabric_login(
- ha, fcport, &next_loopid);
- } else
- status =
- qla2x00_local_device_login(
- ha, fcport);
-
- fcport->login_retry--;
- if (status == QLA_SUCCESS) {
- fcport->old_loop_id = fcport->loop_id;
-
- DEBUG(printk("scsi(%ld): port login OK: logged in ID 0x%x\n",
- ha->host_no, fcport->loop_id));
-
- qla2x00_update_fcport(ha,
- fcport);
- } else if (status == 1) {
- set_bit(RELOGIN_NEEDED, &ha->dpc_flags);
- /* retry the login again */
- DEBUG(printk("scsi(%ld): Retrying %d login again loop_id 0x%x\n",
- ha->host_no,
- fcport->login_retry, fcport->loop_id));
- } else {
- fcport->login_retry = 0;
- }
- if (fcport->login_retry == 0 && status != QLA_SUCCESS)
- fcport->loop_id = FC_NO_LOOP_ID;
- }
- if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
- break;
- }
+ base_vha->host_no));
+ qla2x00_relogin(base_vha);
+
DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n",
- ha->host_no));
+ base_vha->host_no));
}
- if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+ if (test_and_clear_bit(LOOP_RESYNC_NEEDED,
+ &base_vha->dpc_flags)) {
DEBUG(printk("scsi(%ld): qla2x00_loop_resync()\n",
- ha->host_no));
+ base_vha->host_no));
if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE,
- &ha->dpc_flags))) {
+ &base_vha->dpc_flags))) {
- rval = qla2x00_loop_resync(ha);
+ rval = qla2x00_loop_resync(base_vha);
- clear_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags);
+ clear_bit(LOOP_RESYNC_ACTIVE,
+ &base_vha->dpc_flags);
}
DEBUG(printk("scsi(%ld): qla2x00_loop_resync - end\n",
- ha->host_no));
+ base_vha->host_no));
}
- if (test_bit(NPIV_CONFIG_NEEDED, &ha->dpc_flags) &&
- atomic_read(&ha->loop_state) == LOOP_READY) {
- clear_bit(NPIV_CONFIG_NEEDED, &ha->dpc_flags);
- qla2xxx_flash_npiv_conf(ha);
+ if (test_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags) &&
+ atomic_read(&base_vha->loop_state) == LOOP_READY) {
+ clear_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags);
+ qla2xxx_flash_npiv_conf(base_vha);
}
if (!ha->interrupts_on)
ha->isp_ops->enable_intrs(ha);
- if (test_and_clear_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags))
- ha->isp_ops->beacon_blink(ha);
+ if (test_and_clear_bit(BEACON_BLINK_NEEDED,
+ &base_vha->dpc_flags))
+ ha->isp_ops->beacon_blink(base_vha);
- qla2x00_do_dpc_all_vps(ha);
+ qla2x00_do_dpc_all_vps(base_vha);
ha->dpc_active = 0;
} /* End of while(1) */
- DEBUG(printk("scsi(%ld): DPC handler exiting\n", ha->host_no));
+ DEBUG(printk("scsi(%ld): DPC handler exiting\n", base_vha->host_no));
/*
* Make sure that nobody tries to wake us up again.
@@ -2458,11 +2712,12 @@ qla2x00_do_dpc(void *data)
}
void
-qla2xxx_wake_dpc(scsi_qla_host_t *ha)
+qla2xxx_wake_dpc(struct scsi_qla_host *vha)
{
+ struct qla_hw_data *ha = vha->hw;
struct task_struct *t = ha->dpc_thread;
- if (!test_bit(UNLOADING, &ha->dpc_flags) && t)
+ if (!test_bit(UNLOADING, &vha->dpc_flags) && t)
wake_up_process(t);
}
@@ -2474,26 +2729,26 @@ qla2xxx_wake_dpc(scsi_qla_host_t *ha)
* ha = adapter block pointer.
*/
static void
-qla2x00_rst_aen(scsi_qla_host_t *ha)
+qla2x00_rst_aen(scsi_qla_host_t *vha)
{
- if (ha->flags.online && !ha->flags.reset_active &&
- !atomic_read(&ha->loop_down_timer) &&
- !(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) {
+ if (vha->flags.online && !vha->flags.reset_active &&
+ !atomic_read(&vha->loop_down_timer) &&
+ !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))) {
do {
- clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
/*
* Issue marker command only when we are going to start
* the I/O.
*/
- ha->marker_needed = 1;
- } while (!atomic_read(&ha->loop_down_timer) &&
- (test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags)));
+ vha->marker_needed = 1;
+ } while (!atomic_read(&vha->loop_down_timer) &&
+ (test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags)));
}
}
static void
-qla2x00_sp_free_dma(scsi_qla_host_t *ha, srb_t *sp)
+qla2x00_sp_free_dma(srb_t *sp)
{
struct scsi_cmnd *cmd = sp->cmd;
@@ -2505,11 +2760,11 @@ qla2x00_sp_free_dma(scsi_qla_host_t *ha, srb_t *sp)
}
void
-qla2x00_sp_compl(scsi_qla_host_t *ha, srb_t *sp)
+qla2x00_sp_compl(struct qla_hw_data *ha, srb_t *sp)
{
struct scsi_cmnd *cmd = sp->cmd;
- qla2x00_sp_free_dma(ha, sp);
+ qla2x00_sp_free_dma(sp);
mempool_free(sp, ha->srb_mempool);
@@ -2525,7 +2780,7 @@ qla2x00_sp_compl(scsi_qla_host_t *ha, srb_t *sp)
* Context: Interrupt
***************************************************************************/
void
-qla2x00_timer(scsi_qla_host_t *ha)
+qla2x00_timer(scsi_qla_host_t *vha)
{
unsigned long cpu_flags = 0;
fc_port_t *fcport;
@@ -2533,8 +2788,8 @@ qla2x00_timer(scsi_qla_host_t *ha)
int index;
srb_t *sp;
int t;
- scsi_qla_host_t *pha = to_qla_parent(ha);
-
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
/*
* Ports - Port down timer.
*
@@ -2543,7 +2798,7 @@ qla2x00_timer(scsi_qla_host_t *ha)
* the port it marked DEAD.
*/
t = 0;
- list_for_each_entry(fcport, &ha->fcports, list) {
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (fcport->port_type != FCT_TARGET)
continue;
@@ -2557,7 +2812,7 @@ qla2x00_timer(scsi_qla_host_t *ha)
DEBUG(printk("scsi(%ld): fcport-%d - port retry count: "
"%d remaining\n",
- ha->host_no,
+ vha->host_no,
t, atomic_read(&fcport->port_down_timer)));
}
t++;
@@ -2565,30 +2820,32 @@ qla2x00_timer(scsi_qla_host_t *ha)
/* Loop down handler. */
- if (atomic_read(&ha->loop_down_timer) > 0 &&
- !(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) && ha->flags.online) {
+ if (atomic_read(&vha->loop_down_timer) > 0 &&
+ !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
+ && vha->flags.online) {
- if (atomic_read(&ha->loop_down_timer) ==
- ha->loop_down_abort_time) {
+ if (atomic_read(&vha->loop_down_timer) ==
+ vha->loop_down_abort_time) {
DEBUG(printk("scsi(%ld): Loop Down - aborting the "
"queues before time expire\n",
- ha->host_no));
+ vha->host_no));
- if (!IS_QLA2100(ha) && ha->link_down_timeout)
- atomic_set(&ha->loop_state, LOOP_DEAD);
+ if (!IS_QLA2100(ha) && vha->link_down_timeout)
+ atomic_set(&vha->loop_state, LOOP_DEAD);
/* Schedule an ISP abort to return any tape commands. */
/* NPIV - scan physical port only */
- if (!ha->parent) {
+ if (!vha->vp_idx) {
spin_lock_irqsave(&ha->hardware_lock,
cpu_flags);
+ req = ha->req_q_map[0];
for (index = 1;
index < MAX_OUTSTANDING_COMMANDS;
index++) {
fc_port_t *sfcp;
- sp = ha->outstanding_cmds[index];
+ sp = req->outstanding_cmds[index];
if (!sp)
continue;
sfcp = sp->fcport;
@@ -2596,63 +2853,63 @@ qla2x00_timer(scsi_qla_host_t *ha)
continue;
set_bit(ISP_ABORT_NEEDED,
- &ha->dpc_flags);
+ &vha->dpc_flags);
break;
}
spin_unlock_irqrestore(&ha->hardware_lock,
- cpu_flags);
+ cpu_flags);
}
- set_bit(ABORT_QUEUES_NEEDED, &ha->dpc_flags);
+ set_bit(ABORT_QUEUES_NEEDED, &vha->dpc_flags);
start_dpc++;
}
/* if the loop has been down for 4 minutes, reinit adapter */
- if (atomic_dec_and_test(&ha->loop_down_timer) != 0) {
+ if (atomic_dec_and_test(&vha->loop_down_timer) != 0) {
DEBUG(printk("scsi(%ld): Loop down exceed 4 mins - "
"restarting queues.\n",
- ha->host_no));
+ vha->host_no));
- set_bit(RESTART_QUEUES_NEEDED, &ha->dpc_flags);
+ set_bit(RESTART_QUEUES_NEEDED, &vha->dpc_flags);
start_dpc++;
- if (!(ha->device_flags & DFLG_NO_CABLE) &&
- !ha->parent) {
+ if (!(vha->device_flags & DFLG_NO_CABLE) &&
+ !vha->vp_idx) {
DEBUG(printk("scsi(%ld): Loop down - "
"aborting ISP.\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha,
"Loop down - aborting ISP.\n");
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
}
}
DEBUG3(printk("scsi(%ld): Loop Down - seconds remaining %d\n",
- ha->host_no,
- atomic_read(&ha->loop_down_timer)));
+ vha->host_no,
+ atomic_read(&vha->loop_down_timer)));
}
/* Check if beacon LED needs to be blinked */
if (ha->beacon_blink_led == 1) {
- set_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags);
+ set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags);
start_dpc++;
}
/* Process any deferred work. */
- if (!list_empty(&ha->work_list))
+ if (!list_empty(&vha->work_list))
start_dpc++;
/* Schedule the DPC routine if needed */
- if ((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) ||
- test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) ||
- test_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags) ||
+ if ((test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) ||
+ test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags) ||
start_dpc ||
- test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) ||
- test_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags) ||
- test_bit(VP_DPC_NEEDED, &ha->dpc_flags) ||
- test_bit(RELOGIN_NEEDED, &ha->dpc_flags)))
- qla2xxx_wake_dpc(pha);
+ test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) ||
+ test_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags) ||
+ test_bit(VP_DPC_NEEDED, &vha->dpc_flags) ||
+ test_bit(RELOGIN_NEEDED, &vha->dpc_flags)))
+ qla2xxx_wake_dpc(vha);
- qla2x00_restart_timer(ha, WATCH_INTERVAL);
+ qla2x00_restart_timer(vha, WATCH_INTERVAL);
}
/* Firmware interface routines. */
@@ -2684,8 +2941,9 @@ static struct fw_blob qla_fw_blobs[FW_BLOBS] = {
};
struct fw_blob *
-qla2x00_request_firmware(scsi_qla_host_t *ha)
+qla2x00_request_firmware(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
struct fw_blob *blob;
blob = NULL;
@@ -2709,7 +2967,7 @@ qla2x00_request_firmware(scsi_qla_host_t *ha)
if (request_firmware(&blob->fw, blob->name, &ha->pdev->dev)) {
DEBUG2(printk("scsi(%ld): Failed to load firmware image "
- "(%s).\n", ha->host_no, blob->name));
+ "(%s).\n", vha->host_no, blob->name));
blob->fw = NULL;
blob = NULL;
goto out;
@@ -2754,7 +3012,8 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev)
int risc_paused = 0;
uint32_t stat;
unsigned long flags;
- scsi_qla_host_t *ha = pci_get_drvdata(pdev);
+ scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
+ struct qla_hw_data *ha = base_vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
@@ -2777,7 +3036,7 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev)
if (risc_paused) {
qla_printk(KERN_INFO, ha, "RISC paused -- mmio_enabled, "
"Dumping firmware!\n");
- ha->isp_ops->fw_dump(ha, 0);
+ ha->isp_ops->fw_dump(base_vha, 0);
return PCI_ERS_RESULT_NEED_RESET;
} else
@@ -2788,7 +3047,8 @@ static pci_ers_result_t
qla2xxx_pci_slot_reset(struct pci_dev *pdev)
{
pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT;
- scsi_qla_host_t *ha = pci_get_drvdata(pdev);
+ scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
+ struct qla_hw_data *ha = base_vha->hw;
int rc;
if (ha->mem_only)
@@ -2804,13 +3064,13 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev)
}
pci_set_master(pdev);
- if (ha->isp_ops->pci_config(ha))
+ if (ha->isp_ops->pci_config(base_vha))
return ret;
- set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
- if (qla2x00_abort_isp(ha)== QLA_SUCCESS)
+ set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ if (qla2x00_abort_isp(base_vha) == QLA_SUCCESS)
ret = PCI_ERS_RESULT_RECOVERED;
- clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
return ret;
}
@@ -2818,10 +3078,11 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev)
static void
qla2xxx_pci_resume(struct pci_dev *pdev)
{
- scsi_qla_host_t *ha = pci_get_drvdata(pdev);
+ scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
+ struct qla_hw_data *ha = base_vha->hw;
int ret;
- ret = qla2x00_wait_for_hba_online(ha);
+ ret = qla2x00_wait_for_hba_online(base_vha);
if (ret != QLA_SUCCESS) {
qla_printk(KERN_ERR, ha,
"the device failed to resume I/O "
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index e4af678eb2d6..c538ee1b1a31 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -10,10 +10,6 @@
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
-static uint16_t qla2x00_nvram_request(scsi_qla_host_t *, uint32_t);
-static void qla2x00_nv_deselect(scsi_qla_host_t *);
-static void qla2x00_nv_write(scsi_qla_host_t *, uint16_t);
-
/*
* NVRAM support routines
*/
@@ -23,7 +19,7 @@ static void qla2x00_nv_write(scsi_qla_host_t *, uint16_t);
* @ha: HA context
*/
static void
-qla2x00_lock_nvram_access(scsi_qla_host_t *ha)
+qla2x00_lock_nvram_access(struct qla_hw_data *ha)
{
uint16_t data;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -56,7 +52,7 @@ qla2x00_lock_nvram_access(scsi_qla_host_t *ha)
* @ha: HA context
*/
static void
-qla2x00_unlock_nvram_access(scsi_qla_host_t *ha)
+qla2x00_unlock_nvram_access(struct qla_hw_data *ha)
{
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -67,6 +63,84 @@ qla2x00_unlock_nvram_access(scsi_qla_host_t *ha)
}
/**
+ * qla2x00_nv_write() - Prepare for NVRAM read/write operation.
+ * @ha: HA context
+ * @data: Serial interface selector
+ */
+static void
+qla2x00_nv_write(struct qla_hw_data *ha, uint16_t data)
+{
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_CLOCK |
+ NVR_WRT_ENABLE);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+}
+
+/**
+ * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from
+ * NVRAM.
+ * @ha: HA context
+ * @nv_cmd: NVRAM command
+ *
+ * Bit definitions for NVRAM command:
+ *
+ * Bit 26 = start bit
+ * Bit 25, 24 = opcode
+ * Bit 23-16 = address
+ * Bit 15-0 = write data
+ *
+ * Returns the word read from nvram @addr.
+ */
+static uint16_t
+qla2x00_nvram_request(struct qla_hw_data *ha, uint32_t nv_cmd)
+{
+ uint8_t cnt;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ uint16_t data = 0;
+ uint16_t reg_data;
+
+ /* Send command to NVRAM. */
+ nv_cmd <<= 5;
+ for (cnt = 0; cnt < 11; cnt++) {
+ if (nv_cmd & BIT_31)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ else
+ qla2x00_nv_write(ha, 0);
+ nv_cmd <<= 1;
+ }
+
+ /* Read data from NVRAM. */
+ for (cnt = 0; cnt < 16; cnt++) {
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT | NVR_CLOCK);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ data <<= 1;
+ reg_data = RD_REG_WORD(&reg->nvram);
+ if (reg_data & NVR_DATA_IN)
+ data |= BIT_0;
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ }
+
+ /* Deselect chip. */
+ WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+
+ return data;
+}
+
+
+/**
* qla2x00_get_nvram_word() - Calculates word position in NVRAM and calls the
* request routine to get the word from NVRAM.
* @ha: HA context
@@ -75,7 +149,7 @@ qla2x00_unlock_nvram_access(scsi_qla_host_t *ha)
* Returns the word read from nvram @addr.
*/
static uint16_t
-qla2x00_get_nvram_word(scsi_qla_host_t *ha, uint32_t addr)
+qla2x00_get_nvram_word(struct qla_hw_data *ha, uint32_t addr)
{
uint16_t data;
uint32_t nv_cmd;
@@ -88,13 +162,27 @@ qla2x00_get_nvram_word(scsi_qla_host_t *ha, uint32_t addr)
}
/**
+ * qla2x00_nv_deselect() - Deselect NVRAM operations.
+ * @ha: HA context
+ */
+static void
+qla2x00_nv_deselect(struct qla_hw_data *ha)
+{
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+}
+
+/**
* qla2x00_write_nvram_word() - Write NVRAM data.
* @ha: HA context
* @addr: Address in NVRAM to write
* @data: word to program
*/
static void
-qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data)
+qla2x00_write_nvram_word(struct qla_hw_data *ha, uint32_t addr, uint16_t data)
{
int count;
uint16_t word;
@@ -132,7 +220,7 @@ qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data)
do {
if (!--wait_cnt) {
DEBUG9_10(printk("%s(%ld): NVRAM didn't go ready...\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
break;
}
NVRAM_DELAY();
@@ -150,8 +238,8 @@ qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data)
}
static int
-qla2x00_write_nvram_word_tmo(scsi_qla_host_t *ha, uint32_t addr, uint16_t data,
- uint32_t tmo)
+qla2x00_write_nvram_word_tmo(struct qla_hw_data *ha, uint32_t addr,
+ uint16_t data, uint32_t tmo)
{
int ret, count;
uint16_t word;
@@ -209,102 +297,11 @@ qla2x00_write_nvram_word_tmo(scsi_qla_host_t *ha, uint32_t addr, uint16_t data,
}
/**
- * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from
- * NVRAM.
- * @ha: HA context
- * @nv_cmd: NVRAM command
- *
- * Bit definitions for NVRAM command:
- *
- * Bit 26 = start bit
- * Bit 25, 24 = opcode
- * Bit 23-16 = address
- * Bit 15-0 = write data
- *
- * Returns the word read from nvram @addr.
- */
-static uint16_t
-qla2x00_nvram_request(scsi_qla_host_t *ha, uint32_t nv_cmd)
-{
- uint8_t cnt;
- struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
- uint16_t data = 0;
- uint16_t reg_data;
-
- /* Send command to NVRAM. */
- nv_cmd <<= 5;
- for (cnt = 0; cnt < 11; cnt++) {
- if (nv_cmd & BIT_31)
- qla2x00_nv_write(ha, NVR_DATA_OUT);
- else
- qla2x00_nv_write(ha, 0);
- nv_cmd <<= 1;
- }
-
- /* Read data from NVRAM. */
- for (cnt = 0; cnt < 16; cnt++) {
- WRT_REG_WORD(&reg->nvram, NVR_SELECT | NVR_CLOCK);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
- data <<= 1;
- reg_data = RD_REG_WORD(&reg->nvram);
- if (reg_data & NVR_DATA_IN)
- data |= BIT_0;
- WRT_REG_WORD(&reg->nvram, NVR_SELECT);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
- }
-
- /* Deselect chip. */
- WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
-
- return (data);
-}
-
-/**
- * qla2x00_nv_write() - Clean NVRAM operations.
- * @ha: HA context
- */
-static void
-qla2x00_nv_deselect(scsi_qla_host_t *ha)
-{
- struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
-
- WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
-}
-
-/**
- * qla2x00_nv_write() - Prepare for NVRAM read/write operation.
- * @ha: HA context
- * @data: Serial interface selector
- */
-static void
-qla2x00_nv_write(scsi_qla_host_t *ha, uint16_t data)
-{
- struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
-
- WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
- WRT_REG_WORD(&reg->nvram, data | NVR_SELECT| NVR_CLOCK |
- NVR_WRT_ENABLE);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
- WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
-}
-
-/**
* qla2x00_clear_nvram_protection() -
* @ha: HA context
*/
static int
-qla2x00_clear_nvram_protection(scsi_qla_host_t *ha)
+qla2x00_clear_nvram_protection(struct qla_hw_data *ha)
{
int ret, stat;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -352,9 +349,8 @@ qla2x00_clear_nvram_protection(scsi_qla_host_t *ha)
wait_cnt = NVR_WAIT_CNT;
do {
if (!--wait_cnt) {
- DEBUG9_10(printk("%s(%ld): NVRAM didn't go "
- "ready...\n", __func__,
- ha->host_no));
+ DEBUG9_10(qla_printk(
+ "NVRAM didn't go ready...\n"));
break;
}
NVRAM_DELAY();
@@ -370,7 +366,7 @@ qla2x00_clear_nvram_protection(scsi_qla_host_t *ha)
}
static void
-qla2x00_set_nvram_protection(scsi_qla_host_t *ha, int stat)
+qla2x00_set_nvram_protection(struct qla_hw_data *ha, int stat)
{
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
uint32_t word, wait_cnt;
@@ -412,8 +408,7 @@ qla2x00_set_nvram_protection(scsi_qla_host_t *ha, int stat)
wait_cnt = NVR_WAIT_CNT;
do {
if (!--wait_cnt) {
- DEBUG9_10(printk("%s(%ld): NVRAM didn't go ready...\n",
- __func__, ha->host_no));
+ DEBUG9_10(qla_printk("NVRAM didn't go ready...\n"));
break;
}
NVRAM_DELAY();
@@ -454,7 +449,7 @@ nvram_data_to_access_addr(uint32_t naddr)
}
static uint32_t
-qla24xx_read_flash_dword(scsi_qla_host_t *ha, uint32_t addr)
+qla24xx_read_flash_dword(struct qla_hw_data *ha, uint32_t addr)
{
int rval;
uint32_t cnt, data;
@@ -482,21 +477,20 @@ qla24xx_read_flash_dword(scsi_qla_host_t *ha, uint32_t addr)
}
uint32_t *
-qla24xx_read_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
+qla24xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
uint32_t dwords)
{
uint32_t i;
-
/* Dword reads to flash. */
for (i = 0; i < dwords; i++, faddr++)
- dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha,
+ dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(vha->hw,
flash_data_to_access_addr(faddr)));
return dwptr;
}
static int
-qla24xx_write_flash_dword(scsi_qla_host_t *ha, uint32_t addr, uint32_t data)
+qla24xx_write_flash_dword(struct qla_hw_data *ha, uint32_t addr, uint32_t data)
{
int rval;
uint32_t cnt;
@@ -519,7 +513,7 @@ qla24xx_write_flash_dword(scsi_qla_host_t *ha, uint32_t addr, uint32_t data)
}
static void
-qla24xx_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id,
+qla24xx_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id,
uint8_t *flash_id)
{
uint32_t ids;
@@ -544,7 +538,7 @@ qla24xx_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id,
}
static int
-qla2xxx_find_flt_start(scsi_qla_host_t *ha, uint32_t *start)
+qla2xxx_find_flt_start(scsi_qla_host_t *vha, uint32_t *start)
{
const char *loc, *locations[] = { "DEF", "PCI" };
uint32_t pcihdr, pcids;
@@ -552,6 +546,8 @@ qla2xxx_find_flt_start(scsi_qla_host_t *ha, uint32_t *start)
uint8_t *buf, *bcode, last_image;
uint16_t cnt, chksum, *wptr;
struct qla_flt_location *fltl;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
/*
* FLT-location structure resides after the last PCI region.
@@ -563,20 +559,20 @@ qla2xxx_find_flt_start(scsi_qla_host_t *ha, uint32_t *start)
FA_FLASH_LAYOUT_ADDR;
/* Begin with first PCI expansion ROM header. */
- buf = (uint8_t *)ha->request_ring;
- dcode = (uint32_t *)ha->request_ring;
+ buf = (uint8_t *)req->ring;
+ dcode = (uint32_t *)req->ring;
pcihdr = 0;
last_image = 1;
do {
/* Verify PCI expansion ROM header. */
- qla24xx_read_flash_data(ha, dcode, pcihdr >> 2, 0x20);
+ qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, 0x20);
bcode = buf + (pcihdr % 4);
if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa)
goto end;
/* Locate PCI data structure. */
pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]);
- qla24xx_read_flash_data(ha, dcode, pcids >> 2, 0x20);
+ qla24xx_read_flash_data(vha, dcode, pcids >> 2, 0x20);
bcode = buf + (pcihdr % 4);
/* Validate signature of PCI data structure. */
@@ -591,14 +587,14 @@ qla2xxx_find_flt_start(scsi_qla_host_t *ha, uint32_t *start)
} while (!last_image);
/* Now verify FLT-location structure. */
- fltl = (struct qla_flt_location *)ha->request_ring;
- qla24xx_read_flash_data(ha, dcode, pcihdr >> 2,
+ fltl = (struct qla_flt_location *)req->ring;
+ qla24xx_read_flash_data(vha, dcode, pcihdr >> 2,
sizeof(struct qla_flt_location) >> 2);
if (fltl->sig[0] != 'Q' || fltl->sig[1] != 'F' ||
fltl->sig[2] != 'L' || fltl->sig[3] != 'T')
goto end;
- wptr = (uint16_t *)ha->request_ring;
+ wptr = (uint16_t *)req->ring;
cnt = sizeof(struct qla_flt_location) >> 1;
for (chksum = 0; cnt; cnt--)
chksum += le16_to_cpu(*wptr++);
@@ -619,7 +615,7 @@ end:
}
static void
-qla2xxx_get_flt_info(scsi_qla_host_t *ha, uint32_t flt_addr)
+qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
{
const char *loc, *locations[] = { "DEF", "FLT" };
uint16_t *wptr;
@@ -627,12 +623,14 @@ qla2xxx_get_flt_info(scsi_qla_host_t *ha, uint32_t flt_addr)
uint32_t start;
struct qla_flt_header *flt;
struct qla_flt_region *region;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
ha->flt_region_flt = flt_addr;
- wptr = (uint16_t *)ha->request_ring;
- flt = (struct qla_flt_header *)ha->request_ring;
+ wptr = (uint16_t *)req->ring;
+ flt = (struct qla_flt_header *)req->ring;
region = (struct qla_flt_region *)&flt[1];
- ha->isp_ops->read_optrom(ha, (uint8_t *)ha->request_ring,
+ ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring,
flt_addr << 2, OPTROM_BURST_SIZE);
if (*wptr == __constant_cpu_to_le16(0xffff))
goto no_flash_data;
@@ -720,7 +718,7 @@ done:
}
static void
-qla2xxx_get_fdt_info(scsi_qla_host_t *ha)
+qla2xxx_get_fdt_info(scsi_qla_host_t *vha)
{
#define FLASH_BLK_SIZE_4K 0x1000
#define FLASH_BLK_SIZE_32K 0x8000
@@ -731,10 +729,12 @@ qla2xxx_get_fdt_info(scsi_qla_host_t *ha)
struct qla_fdt_layout *fdt;
uint8_t man_id, flash_id;
uint16_t mid, fid;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
- wptr = (uint16_t *)ha->request_ring;
- fdt = (struct qla_fdt_layout *)ha->request_ring;
- ha->isp_ops->read_optrom(ha, (uint8_t *)ha->request_ring,
+ wptr = (uint16_t *)req->ring;
+ fdt = (struct qla_fdt_layout *)req->ring;
+ ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring,
ha->flt_region_fdt << 2, OPTROM_BURST_SIZE);
if (*wptr == __constant_cpu_to_le16(0xffff))
goto no_flash_data;
@@ -807,38 +807,41 @@ done:
}
int
-qla2xxx_get_flash_info(scsi_qla_host_t *ha)
+qla2xxx_get_flash_info(scsi_qla_host_t *vha)
{
int ret;
uint32_t flt_addr;
+ struct qla_hw_data *ha = vha->hw;
if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha))
return QLA_SUCCESS;
- ret = qla2xxx_find_flt_start(ha, &flt_addr);
+ ret = qla2xxx_find_flt_start(vha, &flt_addr);
if (ret != QLA_SUCCESS)
return ret;
- qla2xxx_get_flt_info(ha, flt_addr);
- qla2xxx_get_fdt_info(ha);
+ qla2xxx_get_flt_info(vha, flt_addr);
+ qla2xxx_get_fdt_info(vha);
return QLA_SUCCESS;
}
void
-qla2xxx_flash_npiv_conf(scsi_qla_host_t *ha)
+qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
{
#define NPIV_CONFIG_SIZE (16*1024)
void *data;
uint16_t *wptr;
uint16_t cnt, chksum;
+ int i;
struct qla_npiv_header hdr;
struct qla_npiv_entry *entry;
+ struct qla_hw_data *ha = vha->hw;
if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha))
return;
- ha->isp_ops->read_optrom(ha, (uint8_t *)&hdr,
+ ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr,
ha->flt_region_npiv_conf << 2, sizeof(struct qla_npiv_header));
if (hdr.version == __constant_cpu_to_le16(0xffff))
return;
@@ -857,7 +860,7 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *ha)
return;
}
- ha->isp_ops->read_optrom(ha, (uint8_t *)data,
+ ha->isp_ops->read_optrom(vha, (uint8_t *)data,
ha->flt_region_npiv_conf << 2, NPIV_CONFIG_SIZE);
cnt = (sizeof(struct qla_npiv_header) + le16_to_cpu(hdr.entries) *
@@ -874,7 +877,7 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *ha)
entry = data + sizeof(struct qla_npiv_header);
cnt = le16_to_cpu(hdr.entries);
- for ( ; cnt; cnt--, entry++) {
+ for (i = 0; cnt; cnt--, entry++, i++) {
uint16_t flags;
struct fc_vport_identifiers vid;
struct fc_vport *vport;
@@ -892,25 +895,29 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *ha)
vid.port_name = wwn_to_u64(entry->port_name);
vid.node_name = wwn_to_u64(entry->node_name);
+ memcpy(&ha->npiv_info[i], entry, sizeof(struct qla_npiv_entry));
+
DEBUG2(qla_printk(KERN_DEBUG, ha, "NPIV[%02x]: wwpn=%llx "
- "wwnn=%llx vf_id=0x%x qos=0x%x.\n", cnt,
- (unsigned long long)vid.port_name,
- (unsigned long long)vid.node_name,
- le16_to_cpu(entry->vf_id), le16_to_cpu(entry->qos)));
-
- vport = fc_vport_create(ha->host, 0, &vid);
- if (!vport)
- qla_printk(KERN_INFO, ha, "NPIV-Config: Failed to "
- "create vport [%02x]: wwpn=%llx wwnn=%llx.\n", cnt,
- (unsigned long long)vid.port_name,
- (unsigned long long)vid.node_name);
+ "wwnn=%llx vf_id=0x%x Q_qos=0x%x F_qos=0x%x.\n", cnt,
+ vid.port_name, vid.node_name, le16_to_cpu(entry->vf_id),
+ entry->q_qos, entry->f_qos));
+
+ if (i < QLA_PRECONFIG_VPORTS) {
+ vport = fc_vport_create(vha->host, 0, &vid);
+ if (!vport)
+ qla_printk(KERN_INFO, ha,
+ "NPIV-Config: Failed to create vport [%02x]: "
+ "wwpn=%llx wwnn=%llx.\n", cnt,
+ vid.port_name, vid.node_name);
+ }
}
done:
kfree(data);
+ ha->npiv_info = NULL;
}
static void
-qla24xx_unprotect_flash(scsi_qla_host_t *ha)
+qla24xx_unprotect_flash(struct qla_hw_data *ha)
{
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
@@ -929,7 +936,7 @@ qla24xx_unprotect_flash(scsi_qla_host_t *ha)
}
static void
-qla24xx_protect_flash(scsi_qla_host_t *ha)
+qla24xx_protect_flash(struct qla_hw_data *ha)
{
uint32_t cnt;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
@@ -955,7 +962,7 @@ skip_wrt_protect:
}
static int
-qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
+qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
uint32_t dwords)
{
int ret;
@@ -965,6 +972,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
dma_addr_t optrom_dma;
void *optrom = NULL;
uint32_t *s, *d;
+ struct qla_hw_data *ha = vha->hw;
ret = QLA_SUCCESS;
@@ -1002,9 +1010,8 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
(fdata & 0xff00) |((fdata << 16) &
0xff0000) | ((fdata >> 16) & 0xff));
if (ret != QLA_SUCCESS) {
- DEBUG9(printk("%s(%ld) Unable to flash "
- "sector: address=%x.\n", __func__,
- ha->host_no, faddr));
+ DEBUG9(qla_printk("Unable to flash sector: "
+ "address=%x.\n", faddr));
break;
}
}
@@ -1016,7 +1023,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
miter < OPTROM_BURST_DWORDS; miter++, s++, d++)
*s = cpu_to_le32(*d);
- ret = qla2x00_load_ram(ha, optrom_dma,
+ ret = qla2x00_load_ram(vha, optrom_dma,
flash_data_to_access_addr(faddr),
OPTROM_BURST_DWORDS);
if (ret != QLA_SUCCESS) {
@@ -1044,7 +1051,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
if (ret != QLA_SUCCESS) {
DEBUG9(printk("%s(%ld) Unable to program flash "
"address=%x data=%x.\n", __func__,
- ha->host_no, faddr, *dwptr));
+ vha->host_no, faddr, *dwptr));
break;
}
@@ -1067,11 +1074,12 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
}
uint8_t *
-qla2x00_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
+qla2x00_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
uint32_t bytes)
{
uint32_t i;
uint16_t *wptr;
+ struct qla_hw_data *ha = vha->hw;
/* Word reads to NVRAM via registers. */
wptr = (uint16_t *)buf;
@@ -1085,7 +1093,7 @@ qla2x00_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
}
uint8_t *
-qla24xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
+qla24xx_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
uint32_t bytes)
{
uint32_t i;
@@ -1094,20 +1102,21 @@ qla24xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
/* Dword reads to flash. */
dwptr = (uint32_t *)buf;
for (i = 0; i < bytes >> 2; i++, naddr++)
- dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha,
+ dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(vha->hw,
nvram_data_to_access_addr(naddr)));
return buf;
}
int
-qla2x00_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
+qla2x00_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
uint32_t bytes)
{
int ret, stat;
uint32_t i;
uint16_t *wptr;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
ret = QLA_SUCCESS;
@@ -1134,12 +1143,13 @@ qla2x00_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
}
int
-qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
+qla24xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
uint32_t bytes)
{
int ret;
uint32_t i;
uint32_t *dwptr;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
ret = QLA_SUCCESS;
@@ -1162,9 +1172,8 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
nvram_data_to_access_addr(naddr),
cpu_to_le32(*dwptr));
if (ret != QLA_SUCCESS) {
- DEBUG9(printk("%s(%ld) Unable to program "
- "nvram address=%x data=%x.\n", __func__,
- ha->host_no, naddr, *dwptr));
+ DEBUG9(qla_printk("Unable to program nvram address=%x "
+ "data=%x.\n", naddr, *dwptr));
break;
}
}
@@ -1182,11 +1191,12 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
}
uint8_t *
-qla25xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
+qla25xx_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
uint32_t bytes)
{
uint32_t i;
uint32_t *dwptr;
+ struct qla_hw_data *ha = vha->hw;
/* Dword reads to flash. */
dwptr = (uint32_t *)buf;
@@ -1199,19 +1209,20 @@ qla25xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
}
int
-qla25xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
+qla25xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
uint32_t bytes)
{
+ struct qla_hw_data *ha = vha->hw;
#define RMW_BUFFER_SIZE (64 * 1024)
uint8_t *dbuf;
dbuf = vmalloc(RMW_BUFFER_SIZE);
if (!dbuf)
return QLA_MEMORY_ALLOC_FAILED;
- ha->isp_ops->read_optrom(ha, dbuf, ha->flt_region_vpd_nvram << 2,
+ ha->isp_ops->read_optrom(vha, dbuf, ha->flt_region_vpd_nvram << 2,
RMW_BUFFER_SIZE);
memcpy(dbuf + (naddr << 2), buf, bytes);
- ha->isp_ops->write_optrom(ha, dbuf, ha->flt_region_vpd_nvram << 2,
+ ha->isp_ops->write_optrom(vha, dbuf, ha->flt_region_vpd_nvram << 2,
RMW_BUFFER_SIZE);
vfree(dbuf);
@@ -1219,7 +1230,7 @@ qla25xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
}
static inline void
-qla2x00_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags)
+qla2x00_flip_colors(struct qla_hw_data *ha, uint16_t *pflags)
{
if (IS_QLA2322(ha)) {
/* Flip all colors. */
@@ -1249,12 +1260,13 @@ qla2x00_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags)
#define PIO_REG(h, r) ((h)->pio_address + offsetof(struct device_reg_2xxx, r))
void
-qla2x00_beacon_blink(struct scsi_qla_host *ha)
+qla2x00_beacon_blink(struct scsi_qla_host *vha)
{
uint16_t gpio_enable;
uint16_t gpio_data;
uint16_t led_color = 0;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -1298,17 +1310,18 @@ qla2x00_beacon_blink(struct scsi_qla_host *ha)
}
int
-qla2x00_beacon_on(struct scsi_qla_host *ha)
+qla2x00_beacon_on(struct scsi_qla_host *vha)
{
uint16_t gpio_enable;
uint16_t gpio_data;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
ha->fw_options[1] |= FO1_DISABLE_GPIO6_7;
- if (qla2x00_set_fw_options(ha, ha->fw_options) != QLA_SUCCESS) {
+ if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"Unable to update fw options (beacon on).\n");
return QLA_FUNCTION_FAILED;
@@ -1354,9 +1367,10 @@ qla2x00_beacon_on(struct scsi_qla_host *ha)
}
int
-qla2x00_beacon_off(struct scsi_qla_host *ha)
+qla2x00_beacon_off(struct scsi_qla_host *vha)
{
int rval = QLA_SUCCESS;
+ struct qla_hw_data *ha = vha->hw;
ha->beacon_blink_led = 0;
@@ -1366,12 +1380,12 @@ qla2x00_beacon_off(struct scsi_qla_host *ha)
else
ha->beacon_color_state = QLA_LED_GRN_ON;
- ha->isp_ops->beacon_blink(ha); /* This turns green LED off */
+ ha->isp_ops->beacon_blink(vha); /* This turns green LED off */
ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
ha->fw_options[1] &= ~FO1_DISABLE_GPIO6_7;
- rval = qla2x00_set_fw_options(ha, ha->fw_options);
+ rval = qla2x00_set_fw_options(vha, ha->fw_options);
if (rval != QLA_SUCCESS)
qla_printk(KERN_WARNING, ha,
"Unable to update fw options (beacon off).\n");
@@ -1380,7 +1394,7 @@ qla2x00_beacon_off(struct scsi_qla_host *ha)
static inline void
-qla24xx_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags)
+qla24xx_flip_colors(struct qla_hw_data *ha, uint16_t *pflags)
{
/* Flip all colors. */
if (ha->beacon_color_state == QLA_LED_ALL_ON) {
@@ -1395,11 +1409,12 @@ qla24xx_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags)
}
void
-qla24xx_beacon_blink(struct scsi_qla_host *ha)
+qla24xx_beacon_blink(struct scsi_qla_host *vha)
{
uint16_t led_color = 0;
uint32_t gpio_data;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
/* Save the Original GPIOD. */
@@ -1428,20 +1443,21 @@ qla24xx_beacon_blink(struct scsi_qla_host *ha)
}
int
-qla24xx_beacon_on(struct scsi_qla_host *ha)
+qla24xx_beacon_on(struct scsi_qla_host *vha)
{
uint32_t gpio_data;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
if (ha->beacon_blink_led == 0) {
/* Enable firmware for update */
ha->fw_options[1] |= ADD_FO1_DISABLE_GPIO_LED_CTRL;
- if (qla2x00_set_fw_options(ha, ha->fw_options) != QLA_SUCCESS)
+ if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS)
return QLA_FUNCTION_FAILED;
- if (qla2x00_get_fw_options(ha, ha->fw_options) !=
+ if (qla2x00_get_fw_options(vha, ha->fw_options) !=
QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"Unable to update fw options (beacon on).\n");
@@ -1469,16 +1485,17 @@ qla24xx_beacon_on(struct scsi_qla_host *ha)
}
int
-qla24xx_beacon_off(struct scsi_qla_host *ha)
+qla24xx_beacon_off(struct scsi_qla_host *vha)
{
uint32_t gpio_data;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
ha->beacon_blink_led = 0;
ha->beacon_color_state = QLA_LED_ALL_ON;
- ha->isp_ops->beacon_blink(ha); /* Will flip to all off. */
+ ha->isp_ops->beacon_blink(vha); /* Will flip to all off. */
/* Give control back to firmware. */
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -1492,13 +1509,13 @@ qla24xx_beacon_off(struct scsi_qla_host *ha)
ha->fw_options[1] &= ~ADD_FO1_DISABLE_GPIO_LED_CTRL;
- if (qla2x00_set_fw_options(ha, ha->fw_options) != QLA_SUCCESS) {
+ if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"Unable to update fw options (beacon off).\n");
return QLA_FUNCTION_FAILED;
}
- if (qla2x00_get_fw_options(ha, ha->fw_options) != QLA_SUCCESS) {
+ if (qla2x00_get_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"Unable to get fw options (beacon off).\n");
return QLA_FUNCTION_FAILED;
@@ -1517,7 +1534,7 @@ qla24xx_beacon_off(struct scsi_qla_host *ha)
* @ha: HA context
*/
static void
-qla2x00_flash_enable(scsi_qla_host_t *ha)
+qla2x00_flash_enable(struct qla_hw_data *ha)
{
uint16_t data;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -1533,7 +1550,7 @@ qla2x00_flash_enable(scsi_qla_host_t *ha)
* @ha: HA context
*/
static void
-qla2x00_flash_disable(scsi_qla_host_t *ha)
+qla2x00_flash_disable(struct qla_hw_data *ha)
{
uint16_t data;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -1554,7 +1571,7 @@ qla2x00_flash_disable(scsi_qla_host_t *ha)
* Returns the byte read from flash @addr.
*/
static uint8_t
-qla2x00_read_flash_byte(scsi_qla_host_t *ha, uint32_t addr)
+qla2x00_read_flash_byte(struct qla_hw_data *ha, uint32_t addr)
{
uint16_t data;
uint16_t bank_select;
@@ -1615,7 +1632,7 @@ qla2x00_read_flash_byte(scsi_qla_host_t *ha, uint32_t addr)
* @data: Data to write
*/
static void
-qla2x00_write_flash_byte(scsi_qla_host_t *ha, uint32_t addr, uint8_t data)
+qla2x00_write_flash_byte(struct qla_hw_data *ha, uint32_t addr, uint8_t data)
{
uint16_t bank_select;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -1678,7 +1695,7 @@ qla2x00_write_flash_byte(scsi_qla_host_t *ha, uint32_t addr, uint8_t data)
* Returns 0 on success, else non-zero.
*/
static int
-qla2x00_poll_flash(scsi_qla_host_t *ha, uint32_t addr, uint8_t poll_data,
+qla2x00_poll_flash(struct qla_hw_data *ha, uint32_t addr, uint8_t poll_data,
uint8_t man_id, uint8_t flash_id)
{
int status;
@@ -1718,8 +1735,8 @@ qla2x00_poll_flash(scsi_qla_host_t *ha, uint32_t addr, uint8_t poll_data,
* Returns 0 on success, else non-zero.
*/
static int
-qla2x00_program_flash_address(scsi_qla_host_t *ha, uint32_t addr, uint8_t data,
- uint8_t man_id, uint8_t flash_id)
+qla2x00_program_flash_address(struct qla_hw_data *ha, uint32_t addr,
+ uint8_t data, uint8_t man_id, uint8_t flash_id)
{
/* Write Program Command Sequence. */
if (IS_OEM_001(ha)) {
@@ -1755,7 +1772,7 @@ qla2x00_program_flash_address(scsi_qla_host_t *ha, uint32_t addr, uint8_t data,
* Returns 0 on success, else non-zero.
*/
static int
-qla2x00_erase_flash(scsi_qla_host_t *ha, uint8_t man_id, uint8_t flash_id)
+qla2x00_erase_flash(struct qla_hw_data *ha, uint8_t man_id, uint8_t flash_id)
{
/* Individual Sector Erase Command Sequence */
if (IS_OEM_001(ha)) {
@@ -1791,7 +1808,7 @@ qla2x00_erase_flash(scsi_qla_host_t *ha, uint8_t man_id, uint8_t flash_id)
* Returns 0 on success, else non-zero.
*/
static int
-qla2x00_erase_flash_sector(scsi_qla_host_t *ha, uint32_t addr,
+qla2x00_erase_flash_sector(struct qla_hw_data *ha, uint32_t addr,
uint32_t sec_mask, uint8_t man_id, uint8_t flash_id)
{
/* Individual Sector Erase Command Sequence */
@@ -1817,7 +1834,7 @@ qla2x00_erase_flash_sector(scsi_qla_host_t *ha, uint32_t addr,
* @flash_id: Flash ID
*/
static void
-qla2x00_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id,
+qla2x00_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id,
uint8_t *flash_id)
{
qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
@@ -1831,8 +1848,8 @@ qla2x00_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id,
}
static void
-qla2x00_read_flash_data(scsi_qla_host_t *ha, uint8_t *tmp_buf, uint32_t saddr,
- uint32_t length)
+qla2x00_read_flash_data(struct qla_hw_data *ha, uint8_t *tmp_buf,
+ uint32_t saddr, uint32_t length)
{
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
uint32_t midpoint, ilength;
@@ -1856,14 +1873,15 @@ qla2x00_read_flash_data(scsi_qla_host_t *ha, uint8_t *tmp_buf, uint32_t saddr,
}
static inline void
-qla2x00_suspend_hba(struct scsi_qla_host *ha)
+qla2x00_suspend_hba(struct scsi_qla_host *vha)
{
int cnt;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
/* Suspend HBA. */
- scsi_block_requests(ha->host);
+ scsi_block_requests(vha->host);
ha->isp_ops->disable_intrs(ha);
set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
@@ -1884,26 +1902,29 @@ qla2x00_suspend_hba(struct scsi_qla_host *ha)
}
static inline void
-qla2x00_resume_hba(struct scsi_qla_host *ha)
+qla2x00_resume_hba(struct scsi_qla_host *vha)
{
+ struct qla_hw_data *ha = vha->hw;
+
/* Resume HBA. */
clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
- qla2xxx_wake_dpc(ha);
- qla2x00_wait_for_hba_online(ha);
- scsi_unblock_requests(ha->host);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ qla2x00_wait_for_hba_online(vha);
+ scsi_unblock_requests(vha->host);
}
uint8_t *
-qla2x00_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
+qla2x00_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
uint32_t offset, uint32_t length)
{
uint32_t addr, midpoint;
uint8_t *data;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
/* Suspend HBA. */
- qla2x00_suspend_hba(ha);
+ qla2x00_suspend_hba(vha);
/* Go with read. */
midpoint = ha->optrom_size / 2;
@@ -1922,13 +1943,13 @@ qla2x00_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
qla2x00_flash_disable(ha);
/* Resume HBA. */
- qla2x00_resume_hba(ha);
+ qla2x00_resume_hba(vha);
return buf;
}
int
-qla2x00_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
+qla2x00_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
uint32_t offset, uint32_t length)
{
@@ -1936,10 +1957,11 @@ qla2x00_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
uint8_t man_id, flash_id, sec_number, data;
uint16_t wd;
uint32_t addr, liter, sec_mask, rest_addr;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
/* Suspend HBA. */
- qla2x00_suspend_hba(ha);
+ qla2x00_suspend_hba(vha);
rval = QLA_SUCCESS;
sec_number = 0;
@@ -2139,55 +2161,58 @@ update_flash:
qla2x00_flash_disable(ha);
/* Resume HBA. */
- qla2x00_resume_hba(ha);
+ qla2x00_resume_hba(vha);
return rval;
}
uint8_t *
-qla24xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
+qla24xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
uint32_t offset, uint32_t length)
{
+ struct qla_hw_data *ha = vha->hw;
+
/* Suspend HBA. */
- scsi_block_requests(ha->host);
+ scsi_block_requests(vha->host);
set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
/* Go with read. */
- qla24xx_read_flash_data(ha, (uint32_t *)buf, offset >> 2, length >> 2);
+ qla24xx_read_flash_data(vha, (uint32_t *)buf, offset >> 2, length >> 2);
/* Resume HBA. */
clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
- scsi_unblock_requests(ha->host);
+ scsi_unblock_requests(vha->host);
return buf;
}
int
-qla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
+qla24xx_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
uint32_t offset, uint32_t length)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
/* Suspend HBA. */
- scsi_block_requests(ha->host);
+ scsi_block_requests(vha->host);
set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
/* Go with write. */
- rval = qla24xx_write_flash_data(ha, (uint32_t *)buf, offset >> 2,
+ rval = qla24xx_write_flash_data(vha, (uint32_t *)buf, offset >> 2,
length >> 2);
/* Resume HBA -- RISC reset needed. */
clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
- qla2xxx_wake_dpc(ha);
- qla2x00_wait_for_hba_online(ha);
- scsi_unblock_requests(ha->host);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ qla2x00_wait_for_hba_online(vha);
+ scsi_unblock_requests(vha->host);
return rval;
}
uint8_t *
-qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
+qla25xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
uint32_t offset, uint32_t length)
{
int rval;
@@ -2195,6 +2220,7 @@ qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
void *optrom;
uint8_t *pbuf;
uint32_t faddr, left, burst;
+ struct qla_hw_data *ha = vha->hw;
if (offset & 0xfff)
goto slow_read;
@@ -2219,7 +2245,7 @@ qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
if (burst > left)
burst = left;
- rval = qla2x00_dump_ram(ha, optrom_dma,
+ rval = qla2x00_dump_ram(vha, optrom_dma,
flash_data_to_access_addr(faddr), burst);
if (rval) {
qla_printk(KERN_WARNING, ha,
@@ -2248,7 +2274,7 @@ qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
return buf;
slow_read:
- return qla24xx_read_optrom_data(ha, buf, offset, length);
+ return qla24xx_read_optrom_data(vha, buf, offset, length);
}
/**
@@ -2270,7 +2296,7 @@ slow_read:
* Returns QLA_SUCCESS on successful retrieval of version.
*/
static void
-qla2x00_get_fcode_version(scsi_qla_host_t *ha, uint32_t pcids)
+qla2x00_get_fcode_version(struct qla_hw_data *ha, uint32_t pcids)
{
int ret = QLA_FUNCTION_FAILED;
uint32_t istart, iend, iter, vend;
@@ -2344,13 +2370,14 @@ qla2x00_get_fcode_version(scsi_qla_host_t *ha, uint32_t pcids)
}
int
-qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
+qla2x00_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
{
int ret = QLA_SUCCESS;
uint8_t code_type, last_image;
uint32_t pcihdr, pcids;
uint8_t *dbyte;
uint16_t *dcode;
+ struct qla_hw_data *ha = vha->hw;
if (!ha->pio_address || !mbuf)
return QLA_FUNCTION_FAILED;
@@ -2370,8 +2397,8 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
if (qla2x00_read_flash_byte(ha, pcihdr) != 0x55 ||
qla2x00_read_flash_byte(ha, pcihdr + 0x01) != 0xaa) {
/* No signature */
- DEBUG2(printk("scsi(%ld): No matching ROM "
- "signature.\n", ha->host_no));
+ DEBUG2(qla_printk(KERN_DEBUG, ha, "No matching ROM "
+ "signature.\n"));
ret = QLA_FUNCTION_FAILED;
break;
}
@@ -2387,8 +2414,8 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
qla2x00_read_flash_byte(ha, pcids + 0x2) != 'I' ||
qla2x00_read_flash_byte(ha, pcids + 0x3) != 'R') {
/* Incorrect header. */
- DEBUG2(printk("%s(): PCI data struct not found "
- "pcir_adr=%x.\n", __func__, pcids));
+ DEBUG2(qla_printk(KERN_INFO, ha, "PCI data struct not "
+ "found pcir_adr=%x.\n", pcids));
ret = QLA_FUNCTION_FAILED;
break;
}
@@ -2402,7 +2429,7 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
qla2x00_read_flash_byte(ha, pcids + 0x12);
ha->bios_revision[1] =
qla2x00_read_flash_byte(ha, pcids + 0x13);
- DEBUG3(printk("%s(): read BIOS %d.%d.\n", __func__,
+ DEBUG3(qla_printk(KERN_DEBUG, ha, "read BIOS %d.%d.\n",
ha->bios_revision[1], ha->bios_revision[0]));
break;
case ROM_CODE_TYPE_FCODE:
@@ -2416,12 +2443,12 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
qla2x00_read_flash_byte(ha, pcids + 0x12);
ha->efi_revision[1] =
qla2x00_read_flash_byte(ha, pcids + 0x13);
- DEBUG3(printk("%s(): read EFI %d.%d.\n", __func__,
+ DEBUG3(qla_printk(KERN_DEBUG, ha, "read EFI %d.%d.\n",
ha->efi_revision[1], ha->efi_revision[0]));
break;
default:
- DEBUG2(printk("%s(): Unrecognized code type %x at "
- "pcids %x.\n", __func__, code_type, pcids));
+ DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized code "
+ "type %x at pcids %x.\n", code_type, pcids));
break;
}
@@ -2441,16 +2468,16 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
qla2x00_read_flash_data(ha, dbyte, ha->flt_region_fw * 4 + 10,
8);
- DEBUG3(printk("%s(%ld): dumping fw ver from flash:\n",
- __func__, ha->host_no));
+ DEBUG3(qla_printk(KERN_DEBUG, ha, "dumping fw ver from "
+ "flash:\n"));
DEBUG3(qla2x00_dump_buffer((uint8_t *)dbyte, 8));
if ((dcode[0] == 0xffff && dcode[1] == 0xffff &&
dcode[2] == 0xffff && dcode[3] == 0xffff) ||
(dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
dcode[3] == 0)) {
- DEBUG2(printk("%s(): Unrecognized fw revision at "
- "%x.\n", __func__, ha->flt_region_fw * 4));
+ DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized fw "
+ "revision at %x.\n", ha->flt_region_fw * 4));
} else {
/* values are in big endian */
ha->fw_revision[0] = dbyte[0] << 16 | dbyte[1];
@@ -2465,7 +2492,7 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
}
int
-qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
+qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
{
int ret = QLA_SUCCESS;
uint32_t pcihdr, pcids;
@@ -2473,6 +2500,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
uint8_t *bcode;
uint8_t code_type, last_image;
int i;
+ struct qla_hw_data *ha = vha->hw;
if (!mbuf)
return QLA_FUNCTION_FAILED;
@@ -2489,12 +2517,12 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
last_image = 1;
do {
/* Verify PCI expansion ROM header. */
- qla24xx_read_flash_data(ha, dcode, pcihdr >> 2, 0x20);
+ qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, 0x20);
bcode = mbuf + (pcihdr % 4);
if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa) {
/* No signature */
- DEBUG2(printk("scsi(%ld): No matching ROM "
- "signature.\n", ha->host_no));
+ DEBUG2(qla_printk(KERN_DEBUG, ha, "No matching ROM "
+ "signature.\n"));
ret = QLA_FUNCTION_FAILED;
break;
}
@@ -2502,15 +2530,15 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
/* Locate PCI data structure. */
pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]);
- qla24xx_read_flash_data(ha, dcode, pcids >> 2, 0x20);
+ qla24xx_read_flash_data(vha, dcode, pcids >> 2, 0x20);
bcode = mbuf + (pcihdr % 4);
/* Validate signature of PCI data structure. */
if (bcode[0x0] != 'P' || bcode[0x1] != 'C' ||
bcode[0x2] != 'I' || bcode[0x3] != 'R') {
/* Incorrect header. */
- DEBUG2(printk("%s(): PCI data struct not found "
- "pcir_adr=%x.\n", __func__, pcids));
+ DEBUG2(qla_printk(KERN_INFO, ha, "PCI data struct not "
+ "found pcir_adr=%x.\n", pcids));
ret = QLA_FUNCTION_FAILED;
break;
}
@@ -2522,26 +2550,26 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
/* Intel x86, PC-AT compatible. */
ha->bios_revision[0] = bcode[0x12];
ha->bios_revision[1] = bcode[0x13];
- DEBUG3(printk("%s(): read BIOS %d.%d.\n", __func__,
+ DEBUG3(qla_printk(KERN_DEBUG, ha, "read BIOS %d.%d.\n",
ha->bios_revision[1], ha->bios_revision[0]));
break;
case ROM_CODE_TYPE_FCODE:
/* Open Firmware standard for PCI (FCode). */
ha->fcode_revision[0] = bcode[0x12];
ha->fcode_revision[1] = bcode[0x13];
- DEBUG3(printk("%s(): read FCODE %d.%d.\n", __func__,
+ DEBUG3(qla_printk(KERN_DEBUG, ha, "read FCODE %d.%d.\n",
ha->fcode_revision[1], ha->fcode_revision[0]));
break;
case ROM_CODE_TYPE_EFI:
/* Extensible Firmware Interface (EFI). */
ha->efi_revision[0] = bcode[0x12];
ha->efi_revision[1] = bcode[0x13];
- DEBUG3(printk("%s(): read EFI %d.%d.\n", __func__,
+ DEBUG3(qla_printk(KERN_DEBUG, ha, "read EFI %d.%d.\n",
ha->efi_revision[1], ha->efi_revision[0]));
break;
default:
- DEBUG2(printk("%s(): Unrecognized code type %x at "
- "pcids %x.\n", __func__, code_type, pcids));
+ DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized code "
+ "type %x at pcids %x.\n", code_type, pcids));
break;
}
@@ -2555,7 +2583,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
dcode = mbuf;
- qla24xx_read_flash_data(ha, dcode, ha->flt_region_fw + 4, 4);
+ qla24xx_read_flash_data(vha, dcode, ha->flt_region_fw + 4, 4);
for (i = 0; i < 4; i++)
dcode[i] = be32_to_cpu(dcode[i]);
@@ -2563,8 +2591,8 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) ||
(dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
dcode[3] == 0)) {
- DEBUG2(printk("%s(): Unrecognized fw version at %x.\n",
- __func__, ha->flt_region_fw));
+ DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized fw "
+ "revision at %x.\n", ha->flt_region_fw * 4));
} else {
ha->fw_revision[0] = dcode[0];
ha->fw_revision[1] = dcode[1];
@@ -2593,8 +2621,9 @@ qla2xxx_is_vpd_valid(uint8_t *pos, uint8_t *end)
}
int
-qla2xxx_get_vpd_field(scsi_qla_host_t *ha, char *key, char *str, size_t size)
+qla2xxx_get_vpd_field(scsi_qla_host_t *vha, char *key, char *str, size_t size)
{
+ struct qla_hw_data *ha = vha->hw;
uint8_t *pos = ha->vpd;
uint8_t *end = pos + ha->vpd_size;
int len = 0;
@@ -2621,9 +2650,10 @@ qla2xxx_get_vpd_field(scsi_qla_host_t *ha, char *key, char *str, size_t size)
}
static int
-qla2xxx_hw_event_store(scsi_qla_host_t *ha, uint32_t *fdata)
+qla2xxx_hw_event_store(scsi_qla_host_t *vha, uint32_t *fdata)
{
uint32_t d[2], faddr;
+ struct qla_hw_data *ha = vha->hw;
/* Locate first empty entry. */
for (;;) {
@@ -2634,7 +2664,7 @@ qla2xxx_hw_event_store(scsi_qla_host_t *ha, uint32_t *fdata)
return QLA_MEMORY_ALLOC_FAILED;
}
- qla24xx_read_flash_data(ha, d, ha->hw_event_ptr, 2);
+ qla24xx_read_flash_data(vha, d, ha->hw_event_ptr, 2);
faddr = flash_data_to_access_addr(ha->hw_event_ptr);
ha->hw_event_ptr += FA_HW_EVENT_ENTRY_SIZE;
if (d[0] == __constant_cpu_to_le32(0xffffffff) &&
@@ -2655,12 +2685,12 @@ qla2xxx_hw_event_store(scsi_qla_host_t *ha, uint32_t *fdata)
}
int
-qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1,
+qla2xxx_hw_event_log(scsi_qla_host_t *vha, uint16_t code, uint16_t d1,
uint16_t d2, uint16_t d3)
{
#define QMARK(a, b, c, d) \
cpu_to_le32(LSB(a) << 24 | LSB(b) << 16 | LSB(c) << 8 | LSB(d))
-
+ struct qla_hw_data *ha = vha->hw;
int rval;
uint32_t marker[2], fdata[4];
@@ -2681,7 +2711,7 @@ qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1,
/* Locate marker. */
ha->hw_event_ptr = ha->flt_region_hw_event;
for (;;) {
- qla24xx_read_flash_data(ha, fdata, ha->hw_event_ptr,
+ qla24xx_read_flash_data(vha, fdata, ha->hw_event_ptr,
4);
if (fdata[0] == __constant_cpu_to_le32(0xffffffff) &&
fdata[1] == __constant_cpu_to_le32(0xffffffff))
@@ -2700,7 +2730,7 @@ qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1,
}
/* No marker, write it. */
if (!ha->flags.hw_event_marker_found) {
- rval = qla2xxx_hw_event_store(ha, marker);
+ rval = qla2xxx_hw_event_store(vha, marker);
if (rval != QLA_SUCCESS) {
DEBUG2(qla_printk(KERN_WARNING, ha,
"HW event -- Failed marker write=%x.!\n",
@@ -2714,7 +2744,7 @@ qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1,
/* Store error. */
fdata[0] = cpu_to_le32(code << 16 | d1);
fdata[1] = cpu_to_le32(d2 << 16 | d3);
- rval = qla2xxx_hw_event_store(ha, fdata);
+ rval = qla2xxx_hw_event_store(vha, fdata);
if (rval != QLA_SUCCESS) {
DEBUG2(qla_printk(KERN_WARNING, ha,
"HW event -- Failed error write=%x.!\n",
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index eea6720adf16..be22f3a09f8d 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -7,9 +7,9 @@
/*
* Driver version
*/
-#define QLA2XXX_VERSION "8.02.01-k9"
+#define QLA2XXX_VERSION "8.02.03-k1"
#define QLA_DRIVER_MAJOR_VER 8
#define QLA_DRIVER_MINOR_VER 2
-#define QLA_DRIVER_PATCH_VER 1
+#define QLA_DRIVER_PATCH_VER 3
#define QLA_DRIVER_BETA_VER 0
diff --git a/drivers/scsi/qlogicfas408.c b/drivers/scsi/qlogicfas408.c
index de7b3bc2cbc9..1ad51552d6b1 100644
--- a/drivers/scsi/qlogicfas408.c
+++ b/drivers/scsi/qlogicfas408.c
@@ -23,7 +23,7 @@
Functions as standalone, loadable, and PCMCIA driver, the latter from
Dave Hinds' PCMCIA package.
- Cleaned up 26/10/2002 by Alan Cox <alan@redhat.com> as part of the 2.5
+ Cleaned up 26/10/2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> as part of the 2.5
SCSI driver cleanup and audit. This driver still needs work on the
following
- Non terminating hardware waits
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index edfaf241c5ba..381838ebd460 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -136,7 +136,7 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
else
eh_timed_out = NULL;
- if (eh_timed_out)
+ if (eh_timed_out) {
rtn = eh_timed_out(scmd);
switch (rtn) {
case BLK_EH_NOT_HANDLED:
@@ -144,6 +144,7 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
default:
return rtn;
}
+ }
if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) {
scmd->result |= DID_TIME_OUT << 16;
@@ -1405,8 +1406,9 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
return ADD_TO_MLQUEUE;
case GOOD:
case COMMAND_TERMINATED:
- case TASK_ABORTED:
return SUCCESS;
+ case TASK_ABORTED:
+ goto maybe_retry;
case CHECK_CONDITION:
rtn = scsi_check_sense(scmd);
if (rtn == NEEDS_RETRY)
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index dc1cfb2fd76b..2ae4f8fc5831 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -94,7 +94,7 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd,
SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", *cmd));
result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0,
- &sshdr, timeout, retries);
+ &sshdr, timeout, retries, NULL);
SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", result));
@@ -270,11 +270,11 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
EXPORT_SYMBOL(scsi_ioctl);
/**
- * scsi_nonblock_ioctl() - Handle SG_SCSI_RESET
+ * scsi_nonblockable_ioctl() - Handle SG_SCSI_RESET
* @sdev: scsi device receiving ioctl
* @cmd: Must be SC_SCSI_RESET
* @arg: pointer to int containing SG_SCSI_RESET_{DEVICE,BUS,HOST}
- * @filp: either NULL or a &struct file which must have the O_NONBLOCK flag.
+ * @ndelay: file mode O_NDELAY flag
*/
int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
void __user *arg, int ndelay)
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 148d3af92aef..f2f51e0333eb 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -183,13 +183,15 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
* @timeout: request timeout in seconds
* @retries: number of times to retry request
* @flags: or into request flags;
+ * @resid: optional residual length
*
* returns the req->errors value which is the scsi_cmnd result
* field.
*/
int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
- unsigned char *sense, int timeout, int retries, int flags)
+ unsigned char *sense, int timeout, int retries, int flags,
+ int *resid)
{
struct request *req;
int write = (data_direction == DMA_TO_DEVICE);
@@ -224,6 +226,8 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
if (unlikely(req->data_len > 0 && req->data_len <= bufflen))
memset(buffer + (bufflen - req->data_len), 0, req->data_len);
+ if (resid)
+ *resid = req->data_len;
ret = req->errors;
out:
blk_put_request(req);
@@ -235,7 +239,8 @@ EXPORT_SYMBOL(scsi_execute);
int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
- struct scsi_sense_hdr *sshdr, int timeout, int retries)
+ struct scsi_sense_hdr *sshdr, int timeout, int retries,
+ int *resid)
{
char *sense = NULL;
int result;
@@ -246,7 +251,7 @@ int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
return DRIVER_ERROR << 24;
}
result = scsi_execute(sdev, cmd, data_direction, buffer, bufflen,
- sense, timeout, retries, 0);
+ sense, timeout, retries, 0, resid);
if (sshdr)
scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, sshdr);
@@ -875,16 +880,24 @@ static void scsi_end_bidi_request(struct scsi_cmnd *cmd)
* (the normal case for most drivers), we don't need
* the logic to deal with cleaning up afterwards.
*
- * We must do one of several things here:
+ * We must call scsi_end_request(). This will finish off
+ * the specified number of sectors. If we are done, the
+ * command block will be released and the queue function
+ * will be goosed. If we are not done then we have to
+ * figure out what to do next:
*
- * a) Call scsi_end_request. This will finish off the
- * specified number of sectors. If we are done, the
- * command block will be released, and the queue
- * function will be goosed. If we are not done, then
- * scsi_end_request will directly goose the queue.
+ * a) We can call scsi_requeue_command(). The request
+ * will be unprepared and put back on the queue. Then
+ * a new command will be created for it. This should
+ * be used if we made forward progress, or if we want
+ * to switch from READ(10) to READ(6) for example.
*
- * b) We can just use scsi_requeue_command() here. This would
- * be used if we just wanted to retry, for example.
+ * b) We can call scsi_queue_insert(). The request will
+ * be put back on the queue and retried using the same
+ * command as before, possibly after a delay.
+ *
+ * c) We can call blk_end_request() with -EIO to fail
+ * the remainder of the request.
*/
void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
{
@@ -896,6 +909,9 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
struct scsi_sense_hdr sshdr;
int sense_valid = 0;
int sense_deferred = 0;
+ enum {ACTION_FAIL, ACTION_REPREP, ACTION_RETRY,
+ ACTION_DELAYED_RETRY} action;
+ char *description = NULL;
if (result) {
sense_valid = scsi_command_normalize_sense(cmd, &sshdr);
@@ -947,10 +963,13 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
return;
this_count = blk_rq_bytes(req);
- /* good_bytes = 0, or (inclusive) there were leftovers and
- * result = 0, so scsi_end_request couldn't retry.
- */
- if (sense_valid && !sense_deferred) {
+ if (host_byte(result) == DID_RESET) {
+ /* Third party bus reset or reset for error recovery
+ * reasons. Just retry the command and see what
+ * happens.
+ */
+ action = ACTION_RETRY;
+ } else if (sense_valid && !sense_deferred) {
switch (sshdr.sense_key) {
case UNIT_ATTENTION:
if (cmd->device->removable) {
@@ -958,16 +977,15 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
* and quietly refuse further access.
*/
cmd->device->changed = 1;
- scsi_end_request(cmd, -EIO, this_count, 1);
- return;
+ description = "Media Changed";
+ action = ACTION_FAIL;
} else {
/* Must have been a power glitch, or a
* bus reset. Could not have been a
* media change, so we just retry the
- * request and see what happens.
+ * command and see what happens.
*/
- scsi_requeue_command(q, cmd);
- return;
+ action = ACTION_RETRY;
}
break;
case ILLEGAL_REQUEST:
@@ -983,21 +1001,18 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
sshdr.asc == 0x20 && sshdr.ascq == 0x00) &&
(cmd->cmnd[0] == READ_10 ||
cmd->cmnd[0] == WRITE_10)) {
+ /* This will issue a new 6-byte command. */
cmd->device->use_10_for_rw = 0;
- /* This will cause a retry with a
- * 6-byte command.
- */
- scsi_requeue_command(q, cmd);
- } else if (sshdr.asc == 0x10) /* DIX */
- scsi_end_request(cmd, -EIO, this_count, 0);
- else
- scsi_end_request(cmd, -EIO, this_count, 1);
- return;
+ action = ACTION_REPREP;
+ } else
+ action = ACTION_FAIL;
+ break;
case ABORTED_COMMAND:
if (sshdr.asc == 0x10) { /* DIF */
- scsi_end_request(cmd, -EIO, this_count, 0);
- return;
- }
+ action = ACTION_FAIL;
+ description = "Data Integrity Failure";
+ } else
+ action = ACTION_RETRY;
break;
case NOT_READY:
/* If the device is in the process of becoming
@@ -1012,49 +1027,57 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
case 0x07: /* operation in progress */
case 0x08: /* Long write in progress */
case 0x09: /* self test in progress */
- scsi_requeue_command(q, cmd);
- return;
- default:
+ action = ACTION_DELAYED_RETRY;
break;
}
+ } else {
+ description = "Device not ready";
+ action = ACTION_FAIL;
}
- if (!(req->cmd_flags & REQ_QUIET))
- scsi_cmd_print_sense_hdr(cmd,
- "Device not ready",
- &sshdr);
-
- scsi_end_request(cmd, -EIO, this_count, 1);
- return;
+ break;
case VOLUME_OVERFLOW:
- if (!(req->cmd_flags & REQ_QUIET)) {
- scmd_printk(KERN_INFO, cmd,
- "Volume overflow, CDB: ");
- __scsi_print_command(cmd->cmnd);
- scsi_print_sense("", cmd);
- }
/* See SSC3rXX or current. */
- scsi_end_request(cmd, -EIO, this_count, 1);
- return;
+ action = ACTION_FAIL;
+ break;
default:
+ description = "Unhandled sense code";
+ action = ACTION_FAIL;
break;
}
+ } else {
+ description = "Unhandled error code";
+ action = ACTION_FAIL;
}
- if (host_byte(result) == DID_RESET) {
- /* Third party bus reset or reset for error recovery
- * reasons. Just retry the request and see what
- * happens.
- */
- scsi_requeue_command(q, cmd);
- return;
- }
- if (result) {
+
+ switch (action) {
+ case ACTION_FAIL:
+ /* Give up and fail the remainder of the request */
if (!(req->cmd_flags & REQ_QUIET)) {
+ if (description)
+ scmd_printk(KERN_INFO, cmd, "%s",
+ description);
scsi_print_result(cmd);
if (driver_byte(result) & DRIVER_SENSE)
scsi_print_sense("", cmd);
}
+ blk_end_request(req, -EIO, blk_rq_bytes(req));
+ scsi_next_command(cmd);
+ break;
+ case ACTION_REPREP:
+ /* Unprep the request and put it back at the head of the queue.
+ * A new command will be prepared and issued.
+ */
+ scsi_requeue_command(q, cmd);
+ break;
+ case ACTION_RETRY:
+ /* Retry the same command immediately */
+ scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY);
+ break;
+ case ACTION_DELAYED_RETRY:
+ /* Retry the same command after a delay */
+ scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
+ break;
}
- scsi_end_request(cmd, -EIO, this_count, !result);
}
static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb,
@@ -1998,7 +2021,7 @@ scsi_mode_select(struct scsi_device *sdev, int pf, int sp, int modepage,
}
ret = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, real_buffer, len,
- sshdr, timeout, retries);
+ sshdr, timeout, retries, NULL);
kfree(real_buffer);
return ret;
}
@@ -2063,7 +2086,7 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
memset(buffer, 0, len);
result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer, len,
- sshdr, timeout, retries);
+ sshdr, timeout, retries, NULL);
/* This code looks awful: what it's doing is making sure an
* ILLEGAL REQUEST sense return identifies the actual command
@@ -2145,7 +2168,7 @@ scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries,
/* try to eat the UNIT_ATTENTION if there are enough retries */
do {
result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, sshdr,
- timeout, retries);
+ timeout, retries, NULL);
if (sdev->removable && scsi_sense_valid(sshdr) &&
sshdr->sense_key == UNIT_ATTENTION)
sdev->changed = 1;
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index b14dc02c3ded..18486b51668d 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -216,7 +216,7 @@ static void scsi_unlock_floptical(struct scsi_device *sdev,
scsi_cmd[4] = 0x2a; /* size */
scsi_cmd[5] = 0;
scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, result, 0x2a, NULL,
- SCSI_TIMEOUT, 3);
+ SCSI_TIMEOUT, 3, NULL);
}
/**
@@ -573,6 +573,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
/* Each pass gets up to three chances to ignore Unit Attention */
for (count = 0; count < 3; ++count) {
+ int resid;
+
memset(scsi_cmd, 0, 6);
scsi_cmd[0] = INQUIRY;
scsi_cmd[4] = (unsigned char) try_inquiry_len;
@@ -581,7 +583,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE,
inq_result, try_inquiry_len, &sshdr,
- HZ / 2 + HZ * scsi_inq_timeout, 3);
+ HZ / 2 + HZ * scsi_inq_timeout, 3,
+ &resid);
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s "
"with code 0x%x\n",
@@ -602,6 +605,14 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
(sshdr.ascq == 0))
continue;
}
+ } else {
+ /*
+ * if nothing was transferred, we try
+ * again. It's a workaround for some USB
+ * devices.
+ */
+ if (resid == try_inquiry_len)
+ continue;
}
break;
}
@@ -1390,7 +1401,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE,
lun_data, length, &sshdr,
- SCSI_TIMEOUT + 4 * HZ, 3);
+ SCSI_TIMEOUT + 4 * HZ, 3, NULL);
SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUNS"
" %s (try %d) result 0x%x\n", result
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 1e71abf0607a..062304de4854 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -3012,6 +3012,16 @@ fc_timeout_deleted_rport(struct work_struct *work)
rport->port_state = FC_PORTSTATE_NOTPRESENT;
rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT;
+ /*
+ * Pre-emptively kill I/O rather than waiting for the work queue
+ * item to teardown the starget. (FCOE libFC folks prefer this
+ * and to have the rport_port_id still set when it's done).
+ */
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ fc_terminate_rport_io(rport);
+
+ BUG_ON(rport->port_state != FC_PORTSTATE_NOTPRESENT);
+
/* remove the identifiers that aren't used in the consisting binding */
switch (fc_host->tgtid_bind_type) {
case FC_TGTID_BIND_BY_WWPN:
@@ -3035,9 +3045,6 @@ fc_timeout_deleted_rport(struct work_struct *work)
* went away and didn't come back - we'll remove
* all attached scsi devices.
*/
- spin_unlock_irqrestore(shost->host_lock, flags);
-
- scsi_target_unblock(&rport->dev);
fc_queue_work(shost, &rport->stgt_delete_work);
}
diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c
index 7c2d28924d2a..f49f55c6bfc8 100644
--- a/drivers/scsi/scsi_transport_spi.c
+++ b/drivers/scsi/scsi_transport_spi.c
@@ -111,8 +111,9 @@ static int spi_execute(struct scsi_device *sdev, const void *cmd,
sense, DV_TIMEOUT, /* retries */ 1,
REQ_FAILFAST_DEV |
REQ_FAILFAST_TRANSPORT |
- REQ_FAILFAST_DRIVER);
- if (result & DRIVER_SENSE) {
+ REQ_FAILFAST_DRIVER,
+ NULL);
+ if (driver_byte(result) & DRIVER_SENSE) {
struct scsi_sense_hdr sshdr_tmp;
if (!sshdr)
sshdr = &sshdr_tmp;
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 5081b3981d3c..62b28d58e65e 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -884,7 +884,7 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
* flush everything.
*/
res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES);
+ SD_TIMEOUT, SD_MAX_RETRIES, NULL);
if (res == 0)
break;
}
@@ -1134,7 +1134,7 @@ sd_spinup_disk(struct scsi_disk *sdkp)
the_result = scsi_execute_req(sdkp->device, cmd,
DMA_NONE, NULL, 0,
&sshdr, SD_TIMEOUT,
- SD_MAX_RETRIES);
+ SD_MAX_RETRIES, NULL);
/*
* If the drive has indicated to us that it
@@ -1192,7 +1192,8 @@ sd_spinup_disk(struct scsi_disk *sdkp)
cmd[4] |= 1 << 4;
scsi_execute_req(sdkp->device, cmd, DMA_NONE,
NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES);
+ SD_TIMEOUT, SD_MAX_RETRIES,
+ NULL);
spintime_expire = jiffies + 100 * HZ;
spintime = 1;
}
@@ -1306,7 +1307,7 @@ repeat:
the_result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE,
buffer, longrc ? 13 : 8, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES);
+ SD_TIMEOUT, SD_MAX_RETRIES, NULL);
if (media_not_present(sdkp, &sshdr))
return;
@@ -1986,7 +1987,7 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start)
return -ENODEV;
res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES);
+ SD_TIMEOUT, SD_MAX_RETRIES, NULL);
if (res) {
sd_printk(KERN_WARNING, sdkp, "START_STOP FAILED\n");
sd_print_result(sdkp, res);
diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c
index 1bcf3c33d7ff..7f0df29f3a64 100644
--- a/drivers/scsi/ses.c
+++ b/drivers/scsi/ses.c
@@ -77,7 +77,7 @@ static int ses_recv_diag(struct scsi_device *sdev, int page_code,
};
return scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen,
- NULL, SES_TIMEOUT, SES_RETRIES);
+ NULL, SES_TIMEOUT, SES_RETRIES, NULL);
}
static int ses_send_diag(struct scsi_device *sdev, int page_code,
@@ -95,7 +95,7 @@ static int ses_send_diag(struct scsi_device *sdev, int page_code,
};
result = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, buf, bufflen,
- NULL, SES_TIMEOUT, SES_RETRIES);
+ NULL, SES_TIMEOUT, SES_RETRIES, NULL);
if (result)
sdev_printk(KERN_ERR, sdev, "SEND DIAGNOSTIC result: %8x\n",
result);
@@ -369,7 +369,8 @@ static void ses_match_to_enclosure(struct enclosure_device *edev,
return;
if (scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf,
- VPD_INQUIRY_SIZE, NULL, SES_TIMEOUT, SES_RETRIES))
+ VPD_INQUIRY_SIZE, NULL, SES_TIMEOUT, SES_RETRIES,
+ NULL))
goto free;
vpd_len = (buf[2] << 8) + buf[3];
@@ -380,7 +381,7 @@ static void ses_match_to_enclosure(struct enclosure_device *edev,
cmd[3] = vpd_len >> 8;
cmd[4] = vpd_len & 0xff;
if (scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf,
- vpd_len, NULL, SES_TIMEOUT, SES_RETRIES))
+ vpd_len, NULL, SES_TIMEOUT, SES_RETRIES, NULL))
goto free;
desc = buf + 4;
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 45b66b98a516..e7fa3caead79 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -177,7 +177,7 @@ int sr_test_unit_ready(struct scsi_device *sdev, struct scsi_sense_hdr *sshdr)
do {
the_result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL,
0, sshdr, SR_TIMEOUT,
- retries--);
+ retries--, NULL);
if (scsi_sense_valid(sshdr) &&
sshdr->sense_key == UNIT_ATTENTION)
sdev->changed = 1;
@@ -681,7 +681,7 @@ static void get_sectorsize(struct scsi_cd *cd)
/* Do the command and wait.. */
the_result = scsi_execute_req(cd->device, cmd, DMA_FROM_DEVICE,
buffer, sizeof(buffer), NULL,
- SR_TIMEOUT, MAX_RETRIES);
+ SR_TIMEOUT, MAX_RETRIES, NULL);
retries--;
diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c
index ae87d08df588..d92ff512d213 100644
--- a/drivers/scsi/sr_ioctl.c
+++ b/drivers/scsi/sr_ioctl.c
@@ -207,7 +207,7 @@ int sr_do_ioctl(Scsi_CD *cd, struct packet_command *cgc)
memset(sense, 0, sizeof(*sense));
result = scsi_execute(SDev, cgc->cmd, cgc->data_direction,
cgc->buffer, cgc->buflen, (char *)sense,
- cgc->timeout, IOCTL_RETRIES, 0);
+ cgc->timeout, IOCTL_RETRIES, 0, NULL);
scsi_normalize_sense((char *)sense, sizeof(*sense), &sshdr);
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index c959bdc55f4f..7f3f317ee6ca 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -451,9 +451,23 @@ static void st_sleep_done(void *data, char *sense, int result, int resid)
complete(SRpnt->waiting);
}
-static struct st_request *st_allocate_request(void)
+static struct st_request *st_allocate_request(struct scsi_tape *stp)
{
- return kzalloc(sizeof(struct st_request), GFP_KERNEL);
+ struct st_request *streq;
+
+ streq = kzalloc(sizeof(*streq), GFP_KERNEL);
+ if (streq)
+ streq->stp = stp;
+ else {
+ DEBC(printk(KERN_ERR "%s: Can't get SCSI request.\n",
+ tape_name(stp)););
+ if (signal_pending(current))
+ stp->buffer->syscall_result = -EINTR;
+ else
+ stp->buffer->syscall_result = -EBUSY;
+ }
+
+ return streq;
}
static void st_release_request(struct st_request *streq)
@@ -481,18 +495,10 @@ st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd
return NULL;
}
- if (SRpnt == NULL) {
- SRpnt = st_allocate_request();
- if (SRpnt == NULL) {
- DEBC( printk(KERN_ERR "%s: Can't get SCSI request.\n",
- tape_name(STp)); );
- if (signal_pending(current))
- (STp->buffer)->syscall_result = (-EINTR);
- else
- (STp->buffer)->syscall_result = (-EBUSY);
+ if (!SRpnt) {
+ SRpnt = st_allocate_request(STp);
+ if (!SRpnt)
return NULL;
- }
- SRpnt->stp = STp;
}
/* If async IO, set last_SRpnt. This ptr tells write_behind_check
@@ -527,6 +533,28 @@ st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd
return SRpnt;
}
+static int st_scsi_kern_execute(struct st_request *streq,
+ const unsigned char *cmd, int data_direction,
+ void *buffer, unsigned bufflen, int timeout,
+ int retries)
+{
+ struct scsi_tape *stp = streq->stp;
+ int ret, resid;
+
+ stp->buffer->cmdstat.have_sense = 0;
+ memcpy(streq->cmd, cmd, sizeof(streq->cmd));
+
+ ret = scsi_execute(stp->device, cmd, data_direction, buffer, bufflen,
+ streq->sense, timeout, retries, 0, &resid);
+ if (driver_byte(ret) & DRIVER_ERROR)
+ return -EBUSY;
+
+ stp->buffer->cmdstat.midlevel_result = streq->result = ret;
+ stp->buffer->cmdstat.residual = resid;
+ stp->buffer->syscall_result = st_chk_result(stp, streq);
+
+ return 0;
+}
/* Handle the write-behind checking (waits for completion). Returns -ENOSPC if
write has been correct but EOM early warning reached, -EIO if write ended in
@@ -599,6 +627,7 @@ static int cross_eof(struct scsi_tape * STp, int forward)
{
struct st_request *SRpnt;
unsigned char cmd[MAX_COMMAND_SIZE];
+ int ret;
cmd[0] = SPACE;
cmd[1] = 0x01; /* Space FileMarks */
@@ -612,19 +641,26 @@ static int cross_eof(struct scsi_tape * STp, int forward)
DEBC(printk(ST_DEB_MSG "%s: Stepping over filemark %s.\n",
tape_name(STp), forward ? "forward" : "backward"));
- SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
- STp->device->timeout, MAX_RETRIES, 1);
+ SRpnt = st_allocate_request(STp);
if (!SRpnt)
- return (STp->buffer)->syscall_result;
+ return STp->buffer->syscall_result;
- st_release_request(SRpnt);
- SRpnt = NULL;
+ ret = st_scsi_kern_execute(SRpnt, cmd, DMA_NONE, NULL, 0,
+ STp->device->request_queue->rq_timeout,
+ MAX_RETRIES);
+ if (ret)
+ goto out;
+
+ ret = STp->buffer->syscall_result;
if ((STp->buffer)->cmdstat.midlevel_result != 0)
printk(KERN_ERR "%s: Stepping over filemark %s failed.\n",
tape_name(STp), forward ? "forward" : "backward");
- return (STp->buffer)->syscall_result;
+out:
+ st_release_request(SRpnt);
+
+ return ret;
}
@@ -657,7 +693,8 @@ static int st_flush_write_buffer(struct scsi_tape * STp)
cmd[4] = blks;
SRpnt = st_do_scsi(NULL, STp, cmd, transfer, DMA_TO_DEVICE,
- STp->device->timeout, MAX_WRITE_RETRIES, 1);
+ STp->device->request_queue->rq_timeout,
+ MAX_WRITE_RETRIES, 1);
if (!SRpnt)
return (STp->buffer)->syscall_result;
@@ -844,21 +881,24 @@ static int test_ready(struct scsi_tape *STp, int do_wait)
int attentions, waits, max_wait, scode;
int retval = CHKRES_READY, new_session = 0;
unsigned char cmd[MAX_COMMAND_SIZE];
- struct st_request *SRpnt = NULL;
+ struct st_request *SRpnt;
struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
+ SRpnt = st_allocate_request(STp);
+ if (!SRpnt)
+ return STp->buffer->syscall_result;
+
max_wait = do_wait ? ST_BLOCK_SECONDS : 0;
for (attentions=waits=0; ; ) {
memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
cmd[0] = TEST_UNIT_READY;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
- STp->long_timeout, MAX_READY_RETRIES, 1);
- if (!SRpnt) {
- retval = (STp->buffer)->syscall_result;
+ retval = st_scsi_kern_execute(SRpnt, cmd, DMA_NONE, NULL, 0,
+ STp->long_timeout,
+ MAX_READY_RETRIES);
+ if (retval)
break;
- }
if (cmdstatp->have_sense) {
@@ -902,8 +942,8 @@ static int test_ready(struct scsi_tape *STp, int do_wait)
break;
}
- if (SRpnt != NULL)
- st_release_request(SRpnt);
+ st_release_request(SRpnt);
+
return retval;
}
@@ -980,16 +1020,24 @@ static int check_tape(struct scsi_tape *STp, struct file *filp)
}
}
+ SRpnt = st_allocate_request(STp);
+ if (!SRpnt) {
+ retval = STp->buffer->syscall_result;
+ goto err_out;
+ }
+
if (STp->omit_blklims)
STp->min_block = STp->max_block = (-1);
else {
memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
cmd[0] = READ_BLOCK_LIMITS;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, 6, DMA_FROM_DEVICE,
- STp->device->timeout, MAX_READY_RETRIES, 1);
- if (!SRpnt) {
- retval = (STp->buffer)->syscall_result;
+ retval = st_scsi_kern_execute(SRpnt, cmd, DMA_FROM_DEVICE,
+ STp->buffer->b_data, 6,
+ STp->device->request_queue->rq_timeout,
+ MAX_READY_RETRIES);
+ if (retval) {
+ st_release_request(SRpnt);
goto err_out;
}
@@ -1013,10 +1061,12 @@ static int check_tape(struct scsi_tape *STp, struct file *filp)
cmd[0] = MODE_SENSE;
cmd[4] = 12;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, 12, DMA_FROM_DEVICE,
- STp->device->timeout, MAX_READY_RETRIES, 1);
- if (!SRpnt) {
- retval = (STp->buffer)->syscall_result;
+ retval = st_scsi_kern_execute(SRpnt, cmd, DMA_FROM_DEVICE,
+ STp->buffer->b_data, 12,
+ STp->device->request_queue->rq_timeout,
+ MAX_READY_RETRIES);
+ if (retval) {
+ st_release_request(SRpnt);
goto err_out;
}
@@ -1246,10 +1296,17 @@ static int st_flush(struct file *filp, fl_owner_t id)
cmd[0] = WRITE_FILEMARKS;
cmd[4] = 1 + STp->two_fm;
- SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
- STp->device->timeout, MAX_WRITE_RETRIES, 1);
+ SRpnt = st_allocate_request(STp);
if (!SRpnt) {
- result = (STp->buffer)->syscall_result;
+ result = STp->buffer->syscall_result;
+ goto out;
+ }
+
+ result = st_scsi_kern_execute(SRpnt, cmd, DMA_NONE, NULL, 0,
+ STp->device->request_queue->rq_timeout,
+ MAX_WRITE_RETRIES);
+ if (result) {
+ st_release_request(SRpnt);
goto out;
}
@@ -1634,7 +1691,8 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
cmd[4] = blks;
SRpnt = st_do_scsi(SRpnt, STp, cmd, transfer, DMA_TO_DEVICE,
- STp->device->timeout, MAX_WRITE_RETRIES, !async_write);
+ STp->device->request_queue->rq_timeout,
+ MAX_WRITE_RETRIES, !async_write);
if (!SRpnt) {
retval = STbp->syscall_result;
goto out;
@@ -1804,7 +1862,8 @@ static long read_tape(struct scsi_tape *STp, long count,
SRpnt = *aSRpnt;
SRpnt = st_do_scsi(SRpnt, STp, cmd, bytes, DMA_FROM_DEVICE,
- STp->device->timeout, MAX_RETRIES, 1);
+ STp->device->request_queue->rq_timeout,
+ MAX_RETRIES, 1);
release_buffering(STp, 1);
*aSRpnt = SRpnt;
if (!SRpnt)
@@ -2213,7 +2272,8 @@ static int st_set_options(struct scsi_tape *STp, long options)
DEBC( printk(KERN_INFO "%s: Long timeout set to %d seconds.\n", name,
(value & ~MT_ST_SET_LONG_TIMEOUT)));
} else {
- STp->device->timeout = value * HZ;
+ blk_queue_rq_timeout(STp->device->request_queue,
+ value * HZ);
DEBC( printk(KERN_INFO "%s: Normal timeout set to %d seconds.\n",
name, value) );
}
@@ -2311,7 +2371,8 @@ static int st_set_options(struct scsi_tape *STp, long options)
static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs)
{
unsigned char cmd[MAX_COMMAND_SIZE];
- struct st_request *SRpnt = NULL;
+ struct st_request *SRpnt;
+ int ret;
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = MODE_SENSE;
@@ -2320,14 +2381,17 @@ static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs)
cmd[2] = page;
cmd[4] = 255;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE,
- STp->device->timeout, 0, 1);
- if (SRpnt == NULL)
- return (STp->buffer)->syscall_result;
+ SRpnt = st_allocate_request(STp);
+ if (!SRpnt)
+ return STp->buffer->syscall_result;
+ ret = st_scsi_kern_execute(SRpnt, cmd, DMA_FROM_DEVICE,
+ STp->buffer->b_data, cmd[4],
+ STp->device->request_queue->rq_timeout,
+ MAX_RETRIES);
st_release_request(SRpnt);
- return (STp->buffer)->syscall_result;
+ return ret ? : STp->buffer->syscall_result;
}
@@ -2335,9 +2399,9 @@ static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs)
in the buffer is correctly formatted. The long timeout is used if slow is non-zero. */
static int write_mode_page(struct scsi_tape *STp, int page, int slow)
{
- int pgo;
+ int pgo, timeout, ret = 0;
unsigned char cmd[MAX_COMMAND_SIZE];
- struct st_request *SRpnt = NULL;
+ struct st_request *SRpnt;
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = MODE_SELECT;
@@ -2351,14 +2415,21 @@ static int write_mode_page(struct scsi_tape *STp, int page, int slow)
(STp->buffer)->b_data[MH_OFF_DEV_SPECIFIC] &= ~MH_BIT_WP;
(STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE,
- (slow ? STp->long_timeout : STp->device->timeout), 0, 1);
- if (SRpnt == NULL)
- return (STp->buffer)->syscall_result;
+ SRpnt = st_allocate_request(STp);
+ if (!SRpnt)
+ return ret;
+
+ timeout = slow ? STp->long_timeout :
+ STp->device->request_queue->rq_timeout;
+
+ ret = st_scsi_kern_execute(SRpnt, cmd, DMA_TO_DEVICE,
+ STp->buffer->b_data, cmd[4], timeout, 0);
+ if (!ret)
+ ret = STp->buffer->syscall_result;
st_release_request(SRpnt);
- return (STp->buffer)->syscall_result;
+ return ret;
}
@@ -2464,7 +2535,7 @@ static int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_cod
}
if (STp->immediate) {
cmd[1] = 1; /* Don't wait for completion */
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
}
else
timeout = STp->long_timeout;
@@ -2476,13 +2547,16 @@ static int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_cod
printk(ST_DEB_MSG "%s: Loading tape.\n", name);
);
- SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
- timeout, MAX_RETRIES, 1);
+ SRpnt = st_allocate_request(STp);
if (!SRpnt)
- return (STp->buffer)->syscall_result;
+ return STp->buffer->syscall_result;
+
+ retval = st_scsi_kern_execute(SRpnt, cmd, DMA_NONE, NULL, 0, timeout,
+ MAX_RETRIES);
+ if (retval)
+ goto out;
retval = (STp->buffer)->syscall_result;
- st_release_request(SRpnt);
if (!retval) { /* SCSI command successful */
@@ -2501,6 +2575,8 @@ static int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_cod
STps = &(STp->ps[STp->partition]);
STps->drv_file = STps->drv_block = (-1);
}
+out:
+ st_release_request(SRpnt);
return retval;
}
@@ -2638,7 +2714,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
cmd[2] = (arg >> 16);
cmd[3] = (arg >> 8);
cmd[4] = arg;
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
DEBC(
if (cmd_in == MTWEOF)
printk(ST_DEB_MSG "%s: Writing %d filemarks.\n", name,
@@ -2656,7 +2732,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
cmd[0] = REZERO_UNIT;
if (STp->immediate) {
cmd[1] = 1; /* Don't wait for completion */
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
}
DEBC(printk(ST_DEB_MSG "%s: Rewinding tape.\n", name));
fileno = blkno = at_sm = 0;
@@ -2669,7 +2745,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
cmd[0] = START_STOP;
if (STp->immediate) {
cmd[1] = 1; /* Don't wait for completion */
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
}
cmd[4] = 3;
DEBC(printk(ST_DEB_MSG "%s: Retensioning tape.\n", name));
@@ -2702,7 +2778,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
cmd[1] = (arg ? 1 : 0); /* Long erase with non-zero argument */
if (STp->immediate) {
cmd[1] |= 2; /* Don't wait for completion */
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
}
else
timeout = STp->long_timeout * 8;
@@ -2754,7 +2830,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
(STp->buffer)->b_data[9] = (ltmp >> 16);
(STp->buffer)->b_data[10] = (ltmp >> 8);
(STp->buffer)->b_data[11] = ltmp;
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
DEBC(
if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK)
printk(ST_DEB_MSG
@@ -2776,12 +2852,15 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
return (-ENOSYS);
}
- SRpnt = st_do_scsi(NULL, STp, cmd, datalen, direction,
- timeout, MAX_RETRIES, 1);
+ SRpnt = st_allocate_request(STp);
if (!SRpnt)
return (STp->buffer)->syscall_result;
- ioctl_result = (STp->buffer)->syscall_result;
+ ioctl_result = st_scsi_kern_execute(SRpnt, cmd, direction,
+ STp->buffer->b_data, datalen,
+ timeout, MAX_RETRIES);
+ if (!ioctl_result)
+ ioctl_result = (STp->buffer)->syscall_result;
if (!ioctl_result) { /* SCSI command successful */
st_release_request(SRpnt);
@@ -2943,10 +3022,17 @@ static int get_location(struct scsi_tape *STp, unsigned int *block, int *partiti
if (!logical && !STp->scsi2_logical)
scmd[1] = 1;
}
- SRpnt = st_do_scsi(NULL, STp, scmd, 20, DMA_FROM_DEVICE,
- STp->device->timeout, MAX_READY_RETRIES, 1);
+
+ SRpnt = st_allocate_request(STp);
if (!SRpnt)
- return (STp->buffer)->syscall_result;
+ return STp->buffer->syscall_result;
+
+ result = st_scsi_kern_execute(SRpnt, scmd, DMA_FROM_DEVICE,
+ STp->buffer->b_data, 20,
+ STp->device->request_queue->rq_timeout,
+ MAX_READY_RETRIES);
+ if (result)
+ goto out;
if ((STp->buffer)->syscall_result != 0 ||
(STp->device->scsi_level >= SCSI_2 &&
@@ -2974,6 +3060,7 @@ static int get_location(struct scsi_tape *STp, unsigned int *block, int *partiti
DEBC(printk(ST_DEB_MSG "%s: Got tape pos. blk %d part %d.\n", name,
*block, *partition));
}
+out:
st_release_request(SRpnt);
SRpnt = NULL;
@@ -3045,13 +3132,17 @@ static int set_location(struct scsi_tape *STp, unsigned int block, int partition
}
if (STp->immediate) {
scmd[1] |= 1; /* Don't wait for completion */
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
}
- SRpnt = st_do_scsi(NULL, STp, scmd, 0, DMA_NONE,
- timeout, MAX_READY_RETRIES, 1);
+ SRpnt = st_allocate_request(STp);
if (!SRpnt)
- return (STp->buffer)->syscall_result;
+ return STp->buffer->syscall_result;
+
+ result = st_scsi_kern_execute(SRpnt, scmd, DMA_NONE, NULL, 0,
+ timeout, MAX_READY_RETRIES);
+ if (result)
+ goto out;
STps->drv_block = STps->drv_file = (-1);
STps->eof = ST_NOEOF;
@@ -3076,7 +3167,7 @@ static int set_location(struct scsi_tape *STp, unsigned int block, int partition
STps->drv_block = STps->drv_file = 0;
result = 0;
}
-
+out:
st_release_request(SRpnt);
SRpnt = NULL;
@@ -4029,7 +4120,7 @@ static int st_probe(struct device *dev)
tpnt->partition = 0;
tpnt->new_partition = 0;
tpnt->nbr_partitions = 0;
- tpnt->device->timeout = ST_TIMEOUT;
+ blk_queue_rq_timeout(tpnt->device->request_queue, ST_TIMEOUT);
tpnt->long_timeout = ST_LONG_TIMEOUT;
tpnt->try_dio = try_direct_io && !SDp->host->unchecked_isa_dma;
diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c
index 2fa830c0be27..a3a18ad73125 100644
--- a/drivers/scsi/stex.c
+++ b/drivers/scsi/stex.c
@@ -1108,8 +1108,7 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_scsi_host_put;
}
- hba->mmio_base = ioremap_nocache(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
+ hba->mmio_base = pci_ioremap_bar(pdev, 0);
if ( !hba->mmio_base) {
printk(KERN_ERR DRV_NAME "(%s): memory map failed\n",
pci_name(pdev));
diff --git a/drivers/scsi/sym53c416.c b/drivers/scsi/sym53c416.c
index f7d279542fa5..e5c369bb568f 100644
--- a/drivers/scsi/sym53c416.c
+++ b/drivers/scsi/sym53c416.c
@@ -6,7 +6,7 @@
* Changes :
*
* Marcelo Tosatti <marcelo@conectiva.com.br> : Added io_request_lock locking
- * Alan Cox <alan@redhat.com> : Cleaned up code formatting
+ * Alan Cox <alan@lxorguk.ukuu.org.uk> : Cleaned up code formatting
* Fixed an irq locking bug
* Added ISAPnP support
* Bjoern A. Zeeb <bzeeb@zabbadoz.net> : Initial irq locking updates
diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c
index 69ac6e590f1d..9a4273445c0d 100644
--- a/drivers/scsi/tmscsim.c
+++ b/drivers/scsi/tmscsim.c
@@ -2572,9 +2572,10 @@ static struct pci_driver dc390_driver = {
static int __init dc390_module_init(void)
{
- if (!disable_clustering)
+ if (!disable_clustering) {
printk(KERN_INFO "DC390: clustering now enabled by default. If you get problems load\n");
printk(KERN_INFO " with \"disable_clustering=1\" and report to maintainers\n");
+ }
if (tmscsim[0] == -1 || tmscsim[0] > 15) {
tmscsim[0] = 7;
diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c
index 329eb8780e74..601e95141cbe 100644
--- a/drivers/scsi/u14-34f.c
+++ b/drivers/scsi/u14-34f.c
@@ -1111,7 +1111,8 @@ static int u14_34f_detect(struct scsi_host_template *tpnt) {
static void map_dma(unsigned int i, unsigned int j) {
unsigned int data_len = 0;
- unsigned int k, count, pci_dir;
+ unsigned int k, pci_dir;
+ int count;
struct scatterlist *sg;
struct mscp *cpp;
struct scsi_cmnd *SCpnt;
diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c
index d4c13561f4a6..093610bcfcce 100644
--- a/drivers/scsi/wd7000.c
+++ b/drivers/scsi/wd7000.c
@@ -146,13 +146,13 @@
*
* use host->host_lock, not io_request_lock, cleanups
*
- * 2002/10/04 - Alan Cox <alan@redhat.com>
+ * 2002/10/04 - Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* Use dev_id for interrupts, kill __func__ pasting
* Add a lock for the scb pool, clean up all other cli/sti usage stuff
* Use the adapter lock for the other places we had the cli's
*
- * 2002/10/06 - Alan Cox <alan@redhat.com>
+ * 2002/10/06 - Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* Switch to new style error handling
* Clean up delay to udelay, and yielding sleeps
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 579d63a81aa2..b695ab3142d8 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -447,7 +447,7 @@ config SERIAL_CLPS711X_CONSOLE
config SERIAL_SAMSUNG
tristate "Samsung SoC serial support"
- depends on ARM && PLAT_S3C24XX
+ depends on ARM && PLAT_S3C
select SERIAL_CORE
help
Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
@@ -455,6 +455,16 @@ config SERIAL_SAMSUNG
provide all of these ports, depending on how the serial port
pins are configured.
+config SERIAL_SAMSUNG_UARTS
+ int
+ depends on SERIAL_SAMSUNG
+ default 2 if ARCH_S3C2400
+ default 4 if ARCH_S3C64XX || CPU_S3C2443
+ default 3
+ help
+ Select the number of available UART ports for the Samsung S3C
+ serial driver
+
config SERIAL_SAMSUNG_DEBUG
bool "Samsung SoC serial debug"
depends on SERIAL_SAMSUNG && DEBUG_LL
@@ -508,7 +518,20 @@ config SERIAL_S3C2440
help
Serial port support for the Samsung S3C2440 and S3C2442 SoC
+config SERIAL_S3C24A0
+ tristate "Samsung S3C24A0 Serial port support"
+ depends on SERIAL_SAMSUNG && CPU_S3C24A0
+ default y if CPU_S3C24A0
+ help
+ Serial port support for the Samsung S3C24A0 SoC
+config SERIAL_S3C6400
+ tristate "Samsung S3C6400/S3C6410 Serial port support"
+ depends on SERIAL_SAMSUNG && (CPU_S3C600 || CPU_S3C6410)
+ default y
+ help
+ Serial port support for the Samsung S3C6400 and S3C6410
+ SoCs
config SERIAL_DZ
bool "DECstation DZ serial driver"
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 0c17c8ddb19d..dfe775ac45b2 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -41,6 +41,8 @@ obj-$(CONFIG_SERIAL_S3C2400) += s3c2400.o
obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o
obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o
obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o
+obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o
+obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o
obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
obj-$(CONFIG_SERIAL_MUX) += mux.o
obj-$(CONFIG_SERIAL_68328) += 68328serial.o
diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c
index 71562689116f..e3a5ad5ef1d6 100644
--- a/drivers/serial/amba-pl010.c
+++ b/drivers/serial/amba-pl010.c
@@ -692,7 +692,7 @@ static int pl010_probe(struct amba_device *dev, void *id)
goto free;
}
- uap->clk = clk_get(&dev->dev, "UARTCLK");
+ uap->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(uap->clk)) {
ret = PTR_ERR(uap->clk);
goto unmap;
diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c
index b7180046f8db..8b2b9700f3e4 100644
--- a/drivers/serial/amba-pl011.c
+++ b/drivers/serial/amba-pl011.c
@@ -756,7 +756,7 @@ static int pl011_probe(struct amba_device *dev, void *id)
goto free;
}
- uap->clk = clk_get(&dev->dev, "UARTCLK");
+ uap->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(uap->clk)) {
ret = PTR_ERR(uap->clk);
goto unmap;
diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 3f90f1bbbbcd..a50954612b60 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -66,7 +66,7 @@
#define ONEMS 0xb0 /* One Millisecond register */
#define UTS 0xb4 /* UART Test Register */
#endif
-#ifdef CONFIG_ARCH_IMX
+#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1)
#define BIPR1 0xb0 /* Incremental Preset Register 1 */
#define BIPR2 0xb4 /* Incremental Preset Register 2 */
#define BIPR3 0xb8 /* Incremental Preset Register 3 */
@@ -96,7 +96,7 @@
#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
#define UCR1_SNDBRK (1<<4) /* Send break */
#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
-#ifdef CONFIG_ARCH_IMX
+#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1)
#define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */
#endif
#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
@@ -187,11 +187,11 @@
#define MAX_INTERNAL_IRQ IMX_IRQS
#endif
-#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
+#ifdef CONFIG_ARCH_MXC
#define SERIAL_IMX_MAJOR 207
#define MINOR_START 16
#define DEV_NAME "ttymxc"
-#define MAX_INTERNAL_IRQ MXC_MAX_INT_LINES
+#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS
#endif
/*
diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c
index abc00be55433..f6e3b86bb0be 100644
--- a/drivers/serial/pxa.c
+++ b/drivers/serial/pxa.c
@@ -48,6 +48,7 @@
#include <mach/hardware.h>
#include <asm/irq.h>
#include <mach/pxa-regs.h>
+#include <mach/regs-uart.h>
struct uart_pxa_port {
@@ -766,7 +767,7 @@ static int serial_pxa_probe(struct platform_device *dev)
if (!sport)
return -ENOMEM;
- sport->clk = clk_get(&dev->dev, "UARTCLK");
+ sport->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(sport->clk)) {
ret = PTR_ERR(sport->clk);
goto err_free;
diff --git a/drivers/serial/s3c24a0.c b/drivers/serial/s3c24a0.c
new file mode 100644
index 000000000000..ebf2fd3c8f7d
--- /dev/null
+++ b/drivers/serial/s3c24a0.c
@@ -0,0 +1,118 @@
+/* linux/drivers/serial/s3c24a0.c
+ *
+ * Driver for Samsung S3C24A0 SoC onboard UARTs.
+ *
+ * Based on drivers/serial/s3c2410.c
+ *
+ * Author: Sandeep Patil <sandeep.patil@azingo.com>
+ *
+ * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include <mach/hardware.h>
+
+#include <plat/regs-serial.h>
+#include <mach/regs-gpio.h>
+
+#include "samsung.h"
+
+static int s3c24a0_serial_setsource(struct uart_port *port,
+ struct s3c24xx_uart_clksrc *clk)
+{
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+ if (strcmp(clk->name, "uclk") == 0)
+ ucon |= S3C2410_UCON_UCLK;
+ else
+ ucon &= ~S3C2410_UCON_UCLK;
+
+ wr_regl(port, S3C2410_UCON, ucon);
+ return 0;
+}
+
+static int s3c24a0_serial_getsource(struct uart_port *port,
+ struct s3c24xx_uart_clksrc *clk)
+{
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+ clk->divisor = 1;
+ clk->name = (ucon & S3C2410_UCON_UCLK) ? "uclk" : "pclk";
+
+ return 0;
+}
+
+static int s3c24a0_serial_resetport(struct uart_port *port,
+ struct s3c2410_uartcfg *cfg)
+{
+ dbg("s3c24a0_serial_resetport: port=%p (%08lx), cfg=%p\n",
+ port, port->mapbase, cfg);
+
+ wr_regl(port, S3C2410_UCON, cfg->ucon);
+ wr_regl(port, S3C2410_ULCON, cfg->ulcon);
+
+ /* reset both fifos */
+
+ wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
+ wr_regl(port, S3C2410_UFCON, cfg->ufcon);
+
+ return 0;
+}
+
+static struct s3c24xx_uart_info s3c24a0_uart_inf = {
+ .name = "Samsung S3C24A0 UART",
+ .type = PORT_S3C2410,
+ .fifosize = 16,
+ .rx_fifomask = S3C24A0_UFSTAT_RXMASK,
+ .rx_fifoshift = S3C24A0_UFSTAT_RXSHIFT,
+ .rx_fifofull = S3C24A0_UFSTAT_RXFULL,
+ .tx_fifofull = S3C24A0_UFSTAT_TXFULL,
+ .tx_fifomask = S3C24A0_UFSTAT_TXMASK,
+ .tx_fifoshift = S3C24A0_UFSTAT_TXSHIFT,
+ .get_clksrc = s3c24a0_serial_getsource,
+ .set_clksrc = s3c24a0_serial_setsource,
+ .reset_port = s3c24a0_serial_resetport,
+};
+
+static int s3c24a0_serial_probe(struct platform_device *dev)
+{
+ return s3c24xx_serial_probe(dev, &s3c24a0_uart_inf);
+}
+
+static struct platform_driver s3c24a0_serial_drv = {
+ .probe = s3c24a0_serial_probe,
+ .remove = s3c24xx_serial_remove,
+ .driver = {
+ .name = "s3c24a0-uart",
+ .owner = THIS_MODULE,
+ },
+};
+
+s3c24xx_console_init(&s3c24a0_serial_drv, &s3c24a0_uart_inf);
+
+static int __init s3c24a0_serial_init(void)
+{
+ return s3c24xx_serial_init(&s3c24a0_serial_drv, &s3c24a0_uart_inf);
+}
+
+static void __exit s3c24a0_serial_exit(void)
+{
+ platform_driver_unregister(&s3c24a0_serial_drv);
+}
+
+module_init(s3c24a0_serial_init);
+module_exit(s3c24a0_serial_exit);
+
diff --git a/drivers/serial/s3c6400.c b/drivers/serial/s3c6400.c
new file mode 100644
index 000000000000..06936d13393f
--- /dev/null
+++ b/drivers/serial/s3c6400.c
@@ -0,0 +1,151 @@
+/* linux/drivers/serial/s3c6400.c
+ *
+ * Driver for Samsung S3C6400 and S3C6410 SoC onboard UARTs.
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+
+#include <asm/irq.h>
+#include <mach/hardware.h>
+
+#include <plat/regs-serial.h>
+
+#include "samsung.h"
+
+static int s3c6400_serial_setsource(struct uart_port *port,
+ struct s3c24xx_uart_clksrc *clk)
+{
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+ if (strcmp(clk->name, "uclk0") == 0) {
+ ucon &= ~S3C6400_UCON_CLKMASK;
+ ucon |= S3C6400_UCON_UCLK0;
+ } else if (strcmp(clk->name, "uclk1") == 0)
+ ucon |= S3C6400_UCON_UCLK1;
+ else if (strcmp(clk->name, "pclk") == 0) {
+ /* See notes about transitioning from UCLK to PCLK */
+ ucon &= ~S3C6400_UCON_UCLK0;
+ } else {
+ printk(KERN_ERR "unknown clock source %s\n", clk->name);
+ return -EINVAL;
+ }
+
+ wr_regl(port, S3C2410_UCON, ucon);
+ return 0;
+}
+
+
+static int s3c6400_serial_getsource(struct uart_port *port,
+ struct s3c24xx_uart_clksrc *clk)
+{
+ u32 ucon = rd_regl(port, S3C2410_UCON);
+
+ clk->divisor = 1;
+
+ switch (ucon & S3C6400_UCON_CLKMASK) {
+ case S3C6400_UCON_UCLK0:
+ clk->name = "uclk0";
+ break;
+
+ case S3C6400_UCON_UCLK1:
+ clk->name = "uclk1";
+ break;
+
+ case S3C6400_UCON_PCLK:
+ case S3C6400_UCON_PCLK2:
+ clk->name = "pclk";
+ break;
+ }
+
+ return 0;
+}
+
+static int s3c6400_serial_resetport(struct uart_port *port,
+ struct s3c2410_uartcfg *cfg)
+{
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+ dbg("s3c6400_serial_resetport: port=%p (%08lx), cfg=%p\n",
+ port, port->mapbase, cfg);
+
+ /* ensure we don't change the clock settings... */
+
+ ucon &= S3C6400_UCON_CLKMASK;
+
+ wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);
+ wr_regl(port, S3C2410_ULCON, cfg->ulcon);
+
+ /* reset both fifos */
+
+ wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
+ wr_regl(port, S3C2410_UFCON, cfg->ufcon);
+
+ return 0;
+}
+
+static struct s3c24xx_uart_info s3c6400_uart_inf = {
+ .name = "Samsung S3C6400 UART",
+ .type = PORT_S3C6400,
+ .fifosize = 64,
+ .rx_fifomask = S3C2440_UFSTAT_RXMASK,
+ .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
+ .rx_fifofull = S3C2440_UFSTAT_RXFULL,
+ .tx_fifofull = S3C2440_UFSTAT_TXFULL,
+ .tx_fifomask = S3C2440_UFSTAT_TXMASK,
+ .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
+ .get_clksrc = s3c6400_serial_getsource,
+ .set_clksrc = s3c6400_serial_setsource,
+ .reset_port = s3c6400_serial_resetport,
+};
+
+/* device management */
+
+static int s3c6400_serial_probe(struct platform_device *dev)
+{
+ dbg("s3c6400_serial_probe: dev=%p\n", dev);
+ return s3c24xx_serial_probe(dev, &s3c6400_uart_inf);
+}
+
+static struct platform_driver s3c6400_serial_drv = {
+ .probe = s3c6400_serial_probe,
+ .remove = s3c24xx_serial_remove,
+ .driver = {
+ .name = "s3c6400-uart",
+ .owner = THIS_MODULE,
+ },
+};
+
+s3c24xx_console_init(&s3c6400_serial_drv, &s3c6400_uart_inf);
+
+static int __init s3c6400_serial_init(void)
+{
+ return s3c24xx_serial_init(&s3c6400_serial_drv, &s3c6400_uart_inf);
+}
+
+static void __exit s3c6400_serial_exit(void)
+{
+ platform_driver_unregister(&s3c6400_serial_drv);
+}
+
+module_init(s3c6400_serial_init);
+module_exit(s3c6400_serial_exit);
+
+MODULE_DESCRIPTION("Samsung S3C6400,S3C6410 SoC Serial port driver");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:s3c6400-uart");
diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c
index 1e219d3d0352..41ac94872b8d 100644
--- a/drivers/serial/samsung.c
+++ b/drivers/serial/samsung.c
@@ -42,13 +42,14 @@
#include <linux/serial.h>
#include <linux/delay.h>
#include <linux/clk.h>
+#include <linux/cpufreq.h>
#include <asm/irq.h>
#include <mach/hardware.h>
+#include <mach/map.h>
#include <plat/regs-serial.h>
-#include <mach/regs-gpio.h>
#include "samsung.h"
@@ -58,19 +59,6 @@
#define S3C24XX_SERIAL_MAJOR 204
#define S3C24XX_SERIAL_MINOR 64
-/* we can support 3 uarts, but not always use them */
-
-#ifdef CONFIG_CPU_S3C2400
-#define NR_PORTS (2)
-#else
-#define NR_PORTS (3)
-#endif
-
-/* port irq numbers */
-
-#define TX_IRQ(port) ((port)->irq + 1)
-#define RX_IRQ(port) ((port)->irq)
-
/* macros to change one thing to another */
#define tx_enabled(port) ((port)->unused[0])
@@ -136,8 +124,10 @@ static void s3c24xx_serial_rx_disable(struct uart_port *port)
static void s3c24xx_serial_stop_tx(struct uart_port *port)
{
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
+
if (tx_enabled(port)) {
- disable_irq(TX_IRQ(port));
+ disable_irq(ourport->tx_irq);
tx_enabled(port) = 0;
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_enable(port);
@@ -146,11 +136,13 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port)
static void s3c24xx_serial_start_tx(struct uart_port *port)
{
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
+
if (!tx_enabled(port)) {
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_disable(port);
- enable_irq(TX_IRQ(port));
+ enable_irq(ourport->tx_irq);
tx_enabled(port) = 1;
}
}
@@ -158,9 +150,11 @@ static void s3c24xx_serial_start_tx(struct uart_port *port)
static void s3c24xx_serial_stop_rx(struct uart_port *port)
{
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
+
if (rx_enabled(port)) {
dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
- disable_irq(RX_IRQ(port));
+ disable_irq(ourport->rx_irq);
rx_enabled(port) = 0;
}
}
@@ -384,13 +378,13 @@ static void s3c24xx_serial_shutdown(struct uart_port *port)
struct s3c24xx_uart_port *ourport = to_ourport(port);
if (ourport->tx_claimed) {
- free_irq(TX_IRQ(port), ourport);
+ free_irq(ourport->tx_irq, ourport);
tx_enabled(port) = 0;
ourport->tx_claimed = 0;
}
if (ourport->rx_claimed) {
- free_irq(RX_IRQ(port), ourport);
+ free_irq(ourport->rx_irq, ourport);
ourport->rx_claimed = 0;
rx_enabled(port) = 0;
}
@@ -407,12 +401,11 @@ static int s3c24xx_serial_startup(struct uart_port *port)
rx_enabled(port) = 1;
- ret = request_irq(RX_IRQ(port),
- s3c24xx_serial_rx_chars, 0,
+ ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
s3c24xx_serial_portname(port), ourport);
if (ret != 0) {
- printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port));
+ printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
return ret;
}
@@ -422,12 +415,11 @@ static int s3c24xx_serial_startup(struct uart_port *port)
tx_enabled(port) = 1;
- ret = request_irq(TX_IRQ(port),
- s3c24xx_serial_tx_chars, 0,
+ ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
s3c24xx_serial_portname(port), ourport);
if (ret) {
- printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port));
+ printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
goto err;
}
@@ -452,6 +444,8 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
+ ourport->pm_level = level;
+
switch (level) {
case 3:
if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
@@ -661,6 +655,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
ourport->clksrc = clksrc;
ourport->baudclk = clk;
+ ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
}
switch (termios->c_cflag & CSIZE) {
@@ -752,6 +747,8 @@ static const char *s3c24xx_serial_type(struct uart_port *port)
return "S3C2440";
case PORT_S3C2412:
return "S3C2412";
+ case PORT_S3C6400:
+ return "S3C6400/10";
default:
return NULL;
}
@@ -827,14 +824,14 @@ static struct uart_ops s3c24xx_serial_ops = {
static struct uart_driver s3c24xx_uart_drv = {
.owner = THIS_MODULE,
.dev_name = "s3c2410_serial",
- .nr = 3,
+ .nr = CONFIG_SERIAL_SAMSUNG_UARTS,
.cons = S3C24XX_SERIAL_CONSOLE,
.driver_name = S3C24XX_SERIAL_NAME,
.major = S3C24XX_SERIAL_MAJOR,
.minor = S3C24XX_SERIAL_MINOR,
};
-static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
+static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
@@ -859,7 +856,7 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
.line = 1,
}
},
-#if NR_PORTS > 2
+#if CONFIG_SERIAL_SAMSUNG_UARTS > 2
[2] = {
.port = {
@@ -872,6 +869,20 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
.flags = UPF_BOOT_AUTOCONF,
.line = 2,
}
+ },
+#endif
+#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
+ [3] = {
+ .port = {
+ .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
+ .iotype = UPIO_MEM,
+ .irq = IRQ_S3CUART_RX3,
+ .uartclk = 0,
+ .fifosize = 16,
+ .ops = &s3c24xx_serial_ops,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 3,
+ }
}
#endif
};
@@ -890,6 +901,89 @@ static inline int s3c24xx_serial_resetport(struct uart_port *port,
return (info->reset_port)(port, cfg);
}
+
+#ifdef CONFIG_CPU_FREQ
+
+static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct s3c24xx_uart_port *port;
+ struct uart_port *uport;
+
+ port = container_of(nb, struct s3c24xx_uart_port, freq_transition);
+ uport = &port->port;
+
+ /* check to see if port is enabled */
+
+ if (port->pm_level != 0)
+ return 0;
+
+ /* try and work out if the baudrate is changing, we can detect
+ * a change in rate, but we do not have support for detecting
+ * a disturbance in the clock-rate over the change.
+ */
+
+ if (IS_ERR(port->clk))
+ goto exit;
+
+ if (port->baudclk_rate == clk_get_rate(port->clk))
+ goto exit;
+
+ if (val == CPUFREQ_PRECHANGE) {
+ /* we should really shut the port down whilst the
+ * frequency change is in progress. */
+
+ } else if (val == CPUFREQ_POSTCHANGE) {
+ struct ktermios *termios;
+ struct tty_struct *tty;
+
+ if (uport->info == NULL)
+ goto exit;
+
+ tty = uport->info->port.tty;
+
+ if (tty == NULL)
+ goto exit;
+
+ termios = tty->termios;
+
+ if (termios == NULL) {
+ printk(KERN_WARNING "%s: no termios?\n", __func__);
+ goto exit;
+ }
+
+ s3c24xx_serial_set_termios(uport, termios, NULL);
+ }
+
+ exit:
+ return 0;
+}
+
+static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
+{
+ port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition;
+
+ return cpufreq_register_notifier(&port->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
+{
+ cpufreq_unregister_notifier(&port->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
+{
+ return 0;
+}
+
+static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
+{
+}
+#endif
+
/* s3c24xx_serial_init_port
*
* initialise a single serial port from the platform device given
@@ -914,8 +1008,11 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
if (port->mapbase != 0)
return 0;
- if (cfg->hwport > 3)
- return -EINVAL;
+ if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) {
+ printk(KERN_ERR "%s: port %d bigger than %d\n", __func__,
+ cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS);
+ return -ERANGE;
+ }
/* setup info for port */
port->dev = &platdev->dev;
@@ -943,18 +1040,26 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
- port->mapbase = res->start;
- port->membase = S3C24XX_VA_UART + (res->start - S3C24XX_PA_UART);
+ port->mapbase = res->start;
+ port->membase = S3C_VA_UART + res->start - (S3C_PA_UART & 0xfff00000);
ret = platform_get_irq(platdev, 0);
if (ret < 0)
port->irq = 0;
- else
+ else {
port->irq = ret;
+ ourport->rx_irq = ret;
+ ourport->tx_irq = ret + 1;
+ }
+
+ ret = platform_get_irq(platdev, 1);
+ if (ret > 0)
+ ourport->tx_irq = ret;
ourport->clk = clk_get(&platdev->dev, "uart");
- dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld\n",
- port->mapbase, port->membase, port->irq, port->uartclk);
+ dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n",
+ port->mapbase, port->membase, port->irq,
+ ourport->rx_irq, ourport->tx_irq, port->uartclk);
/* reset the fifos (and setup the uart) */
s3c24xx_serial_resetport(port, cfg);
@@ -1002,6 +1107,10 @@ int s3c24xx_serial_probe(struct platform_device *dev,
if (ret < 0)
printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);
+ ret = s3c24xx_serial_cpufreq_register(ourport);
+ if (ret < 0)
+ dev_err(&dev->dev, "failed to add cpufreq notifier\n");
+
return 0;
probe_err:
@@ -1015,6 +1124,7 @@ int s3c24xx_serial_remove(struct platform_device *dev)
struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
if (port) {
+ s3c24xx_serial_cpufreq_deregister(to_ourport(port));
device_remove_file(&dev->dev, &dev_attr_clock_source);
uart_remove_one_port(&s3c24xx_uart_drv, port);
}
@@ -1219,7 +1329,7 @@ static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info)
platdev_ptr = s3c24xx_uart_devs;
- for (i = 0; i < NR_PORTS; i++, ptr++, platdev_ptr++) {
+ for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) {
s3c24xx_serial_init_port(ptr, info, *platdev_ptr);
}
@@ -1240,7 +1350,7 @@ s3c24xx_serial_console_setup(struct console *co, char *options)
/* is this a valid port */
- if (co->index == -1 || co->index >= NR_PORTS)
+ if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS)
co->index = 0;
port = &s3c24xx_serial_ports[co->index].port;
diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h
index 5c92ebbe7d9e..571d6b90d206 100644
--- a/drivers/serial/samsung.h
+++ b/drivers/serial/samsung.h
@@ -33,12 +33,21 @@ struct s3c24xx_uart_info {
struct s3c24xx_uart_port {
unsigned char rx_claimed;
unsigned char tx_claimed;
+ unsigned int pm_level;
+ unsigned long baudclk_rate;
+
+ unsigned int rx_irq;
+ unsigned int tx_irq;
struct s3c24xx_uart_info *info;
struct s3c24xx_uart_clksrc *clksrc;
struct clk *clk;
struct clk *baudclk;
struct uart_port port;
+
+#ifdef CONFIG_CPU_FREQ
+ struct notifier_block freq_transition;
+#endif
};
/* conversion functions */
diff --git a/drivers/serial/serial_lh7a40x.c b/drivers/serial/serial_lh7a40x.c
index 61dc8b3daa26..a7bf024a8286 100644
--- a/drivers/serial/serial_lh7a40x.c
+++ b/drivers/serial/serial_lh7a40x.c
@@ -41,9 +41,10 @@
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
+#include <linux/io.h>
-#include <asm/io.h>
#include <asm/irq.h>
+#include <mach/hardware.h>
#define DEV_MAJOR 204
#define DEV_MINOR 16
diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c
index cf12f2d84be2..6104f461a3cd 100644
--- a/drivers/spi/pxa2xx_spi.c
+++ b/drivers/spi/pxa2xx_spi.c
@@ -32,8 +32,8 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/delay.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/hardware.h>
#include <mach/pxa-regs.h>
#include <mach/regs-ssp.h>
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c
index c252cbac00f1..256d18395a23 100644
--- a/drivers/spi/spi_s3c24xx.c
+++ b/drivers/spi/spi_s3c24xx.c
@@ -28,7 +28,7 @@
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
-#include <asm/plat-s3c24xx/regs-spi.h>
+#include <plat/regs-spi.h>
#include <mach/spi.h>
struct s3c24xx_spi {
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index 2dbc0db0b46c..8c5026be79d4 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -2145,7 +2145,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
if (irq < 0)
return -ENODEV;
- dev->clk = clk_get(&pdev->dev, "UDCCLK");
+ dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
retval = PTR_ERR(dev->clk);
goto err_clk;
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
index caa37c95802c..944e4ff641df 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -2226,7 +2226,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev)
udc->dev = &pdev->dev;
udc->mach = pdev->dev.platform_data;
- udc->clk = clk_get(&pdev->dev, "UDCCLK");
+ udc->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(udc->clk)) {
retval = PTR_ERR(udc->clk);
goto err_clk;
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
index 00ba06b44752..8d8d65165983 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -53,8 +53,8 @@
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
-#include <asm/plat-s3c24xx/regs-udc.h>
-#include <asm/plat-s3c24xx/udc.h>
+#include <plat/regs-udc.h>
+#include <plat/udc.h>
#include "s3c2410_udc.h"
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
index 5416cf969005..9d487908012e 100644
--- a/drivers/usb/host/ehci-orion.c
+++ b/drivers/usb/host/ehci-orion.c
@@ -33,8 +33,9 @@
/*
* Implement Orion USB controller specification guidelines
*/
-static void orion_usb_setup(struct usb_hcd *hcd)
+static void orion_usb_phy_v1_setup(struct usb_hcd *hcd)
{
+ /* The below GLs are according to the Orion Errata document */
/*
* Clear interrupt cause and mask
*/
@@ -258,9 +259,19 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev)
ehci_orion_conf_mbus_windows(hcd, pd->dram);
/*
- * setup Orion USB controller
+ * setup Orion USB controller.
*/
- orion_usb_setup(hcd);
+ switch (pd->phy_version) {
+ case EHCI_PHY_NA: /* dont change USB phy settings */
+ break;
+ case EHCI_PHY_ORION:
+ orion_usb_phy_v1_setup(hcd);
+ break;
+ case EHCI_PHY_DD:
+ case EHCI_PHY_KW:
+ default:
+ printk(KERN_WARNING "Orion ehci -USB phy version isn't supported.\n");
+ }
err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED);
if (err)
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index 91697bdb399f..4bbddb73abd9 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -18,6 +18,7 @@
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/gpio.h>
#include <mach/hardware.h>
#include <asm/io.h>
@@ -25,7 +26,6 @@
#include <mach/mux.h>
#include <mach/irqs.h>
-#include <mach/gpio.h>
#include <mach/fpga.h>
#include <mach/usb.h>
@@ -254,8 +254,8 @@ static int ohci_omap_init(struct usb_hcd *hcd)
/* gpio9 for overcurrent detction */
omap_cfg_reg(W8_1610_GPIO9);
- omap_request_gpio(9);
- omap_set_gpio_direction(9, 1 /* IN */);
+ gpio_request(9, "OHCI overcurrent");
+ gpio_direction_input(9);
/* for paranoia's sake: disable USB.PUEN */
omap_cfg_reg(W4_USB_HIGHZ);
@@ -407,7 +407,7 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
put_device(ohci->transceiver->dev);
}
if (machine_is_omap_osk())
- omap_free_gpio(9);
+ gpio_free(9);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index e294d430733b..e44dc2cbca24 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -296,7 +296,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
return -ENXIO;
}
- usb_clk = clk_get(&pdev->dev, "USBCLK");
+ usb_clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(usb_clk))
return PTR_ERR(usb_clk);
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index d0c821992a99..6372f8b17b45 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -362,7 +362,7 @@ endchoice
config FB_ACORN
bool "Acorn VIDC support"
- depends on (FB = y) && ARM && (ARCH_ACORN || ARCH_CLPS7500)
+ depends on (FB = y) && ARM && ARCH_ACORN
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -1817,6 +1817,11 @@ config FB_PXA
If unsure, say N.
+config FB_PXA_OVERLAY
+ bool "Support PXA27x/PXA3xx Overlay(s) as framebuffer"
+ default n
+ depends on FB_PXA && (PXA27x || PXA3xx)
+
config FB_PXA_SMARTPANEL
bool "PXA Smartpanel LCD support"
default n
diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c
index a7a1c891bfa2..2ac52fd8cc11 100644
--- a/drivers/video/amba-clcd.c
+++ b/drivers/video/amba-clcd.c
@@ -343,14 +343,14 @@ static int clcdfb_register(struct clcd_fb *fb)
{
int ret;
- fb->clk = clk_get(&fb->dev->dev, "CLCDCLK");
+ fb->clk = clk_get(&fb->dev->dev, NULL);
if (IS_ERR(fb->clk)) {
ret = PTR_ERR(fb->clk);
goto out;
}
fb->fb.fix.mmio_start = fb->dev->res.start;
- fb->fb.fix.mmio_len = SZ_4K;
+ fb->fb.fix.mmio_len = 4096;
fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len);
if (!fb->regs) {
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index 448d209a0bf2..e6210725b9ab 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -112,6 +112,23 @@ static int vga_video_font_height;
static int vga_scan_lines __read_mostly;
static unsigned int vga_rolled_over;
+int vgacon_text_mode_force = 0;
+
+bool vgacon_text_force(void)
+{
+ return vgacon_text_mode_force ? true : false;
+}
+EXPORT_SYMBOL(vgacon_text_force);
+
+static int __init text_mode(char *str)
+{
+ vgacon_text_mode_force = 1;
+ return 1;
+}
+
+/* force text mode - used by kernel modesetting */
+__setup("nomodeset", text_mode);
+
static int __init no_scroll(char *str)
{
/*
diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c
index 41d62632dcdb..39d5d643a50b 100644
--- a/drivers/video/cyber2000fb.c
+++ b/drivers/video/cyber2000fb.c
@@ -1513,7 +1513,7 @@ static int cyberpro_pci_enable_mmio(struct cfb_info *cfb)
iop = ioremap(0x3000000, 0x5000);
if (iop == NULL) {
- prom_printf("iga5000: cannot map I/O\n");
+ printk(KERN_ERR "iga5000: cannot map I/O\n");
return -ENOMEM;
}
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
index ccd986140c95..d58c68cd456e 100644
--- a/drivers/video/imxfb.c
+++ b/drivers/video/imxfb.c
@@ -1,6 +1,4 @@
/*
- * linux/drivers/video/imxfb.c
- *
* Freescale i.MX Frame Buffer device driver
*
* Copyright (C) 2004 Sascha Hauer, Pengutronix
@@ -16,7 +14,6 @@
* linux-arm-kernel@lists.arm.linux.org.uk
*/
-//#define DEBUG 1
#include <linux/module.h>
#include <linux/kernel.h>
@@ -32,9 +29,8 @@
#include <linux/cpufreq.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/io.h>
-#include <mach/hardware.h>
-#include <asm/io.h>
#include <mach/imxfb.h>
/*
@@ -42,23 +38,150 @@
*/
#define DEBUG_VAR 1
-#include "imxfb.h"
+#define DRIVER_NAME "imx-fb"
+
+#define LCDC_SSA 0x00
+
+#define LCDC_SIZE 0x04
+#define SIZE_XMAX(x) ((((x) >> 4) & 0x3f) << 20)
+#define SIZE_YMAX(y) ((y) & 0x1ff)
+
+#define LCDC_VPW 0x08
+#define VPW_VPW(x) ((x) & 0x3ff)
+
+#define LCDC_CPOS 0x0C
+#define CPOS_CC1 (1<<31)
+#define CPOS_CC0 (1<<30)
+#define CPOS_OP (1<<28)
+#define CPOS_CXP(x) (((x) & 3ff) << 16)
+#define CPOS_CYP(y) ((y) & 0x1ff)
+
+#define LCDC_LCWHB 0x10
+#define LCWHB_BK_EN (1<<31)
+#define LCWHB_CW(w) (((w) & 0x1f) << 24)
+#define LCWHB_CH(h) (((h) & 0x1f) << 16)
+#define LCWHB_BD(x) ((x) & 0xff)
+
+#define LCDC_LCHCC 0x14
+#define LCHCC_CUR_COL_R(r) (((r) & 0x1f) << 11)
+#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 5)
+#define LCHCC_CUR_COL_B(b) ((b) & 0x1f)
+
+#define LCDC_PCR 0x18
+
+#define LCDC_HCR 0x1C
+#define HCR_H_WIDTH(x) (((x) & 0x3f) << 26)
+#define HCR_H_WAIT_1(x) (((x) & 0xff) << 8)
+#define HCR_H_WAIT_2(x) ((x) & 0xff)
+
+#define LCDC_VCR 0x20
+#define VCR_V_WIDTH(x) (((x) & 0x3f) << 26)
+#define VCR_V_WAIT_1(x) (((x) & 0xff) << 8)
+#define VCR_V_WAIT_2(x) ((x) & 0xff)
+
+#define LCDC_POS 0x24
+#define POS_POS(x) ((x) & 1f)
+
+#define LCDC_LSCR1 0x28
+/* bit fields in imxfb.h */
+
+#define LCDC_PWMR 0x2C
+/* bit fields in imxfb.h */
+
+#define LCDC_DMACR 0x30
+/* bit fields in imxfb.h */
+
+#define LCDC_RMCR 0x34
+#define RMCR_LCDC_EN (1<<1)
+#define RMCR_SELF_REF (1<<0)
+
+#define LCDC_LCDICR 0x38
+#define LCDICR_INT_SYN (1<<2)
+#define LCDICR_INT_CON (1)
+
+#define LCDC_LCDISR 0x40
+#define LCDISR_UDR_ERR (1<<3)
+#define LCDISR_ERR_RES (1<<2)
+#define LCDISR_EOF (1<<1)
+#define LCDISR_BOF (1<<0)
+
+/*
+ * These are the bitfields for each
+ * display depth that we support.
+ */
+struct imxfb_rgb {
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+};
+
+struct imxfb_info {
+ struct platform_device *pdev;
+ void __iomem *regs;
-static struct imxfb_rgb def_rgb_16 = {
- .red = { .offset = 8, .length = 4, },
- .green = { .offset = 4, .length = 4, },
- .blue = { .offset = 0, .length = 4, },
- .transp = { .offset = 0, .length = 0, },
+ u_int max_bpp;
+ u_int max_xres;
+ u_int max_yres;
+
+ /*
+ * These are the addresses we mapped
+ * the framebuffer memory region to.
+ */
+ dma_addr_t map_dma;
+ u_char *map_cpu;
+ u_int map_size;
+
+ u_char *screen_cpu;
+ dma_addr_t screen_dma;
+ u_int palette_size;
+
+ dma_addr_t dbar1;
+ dma_addr_t dbar2;
+
+ u_int pcr;
+ u_int pwmr;
+ u_int lscr1;
+ u_int dmacr;
+ u_int cmap_inverse:1,
+ cmap_static:1,
+ unused:30;
+
+ void (*lcd_power)(int);
+ void (*backlight_power)(int);
+};
+
+#define IMX_NAME "IMX"
+
+/*
+ * Minimum X and Y resolutions
+ */
+#define MIN_XRES 64
+#define MIN_YRES 64
+
+static struct imxfb_rgb def_rgb_16_tft = {
+ .red = {.offset = 11, .length = 5,},
+ .green = {.offset = 5, .length = 6,},
+ .blue = {.offset = 0, .length = 5,},
+ .transp = {.offset = 0, .length = 0,},
+};
+
+static struct imxfb_rgb def_rgb_16_stn = {
+ .red = {.offset = 8, .length = 4,},
+ .green = {.offset = 4, .length = 4,},
+ .blue = {.offset = 0, .length = 4,},
+ .transp = {.offset = 0, .length = 0,},
};
static struct imxfb_rgb def_rgb_8 = {
- .red = { .offset = 0, .length = 8, },
- .green = { .offset = 0, .length = 8, },
- .blue = { .offset = 0, .length = 8, },
- .transp = { .offset = 0, .length = 0, },
+ .red = {.offset = 0, .length = 8,},
+ .green = {.offset = 0, .length = 8,},
+ .blue = {.offset = 0, .length = 8,},
+ .transp = {.offset = 0, .length = 0,},
};
-static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info);
+static int imxfb_activate_var(struct fb_var_screeninfo *var,
+ struct fb_info *info);
static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
{
@@ -67,10 +190,8 @@ static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
return chan << bf->offset;
}
-#define LCDC_PALETTE(x) __REG2(IMX_LCDC_BASE+0x800, (x)<<2)
-static int
-imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
- u_int trans, struct fb_info *info)
+static int imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int trans, struct fb_info *info)
{
struct imxfb_info *fbi = info->par;
u_int val, ret = 1;
@@ -81,14 +202,13 @@ imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
(CNVT_TOHW(green,4) << 4) |
CNVT_TOHW(blue, 4);
- LCDC_PALETTE(regno) = val;
+ writel(val, fbi->regs + 0x800 + (regno << 2));
ret = 0;
}
return ret;
}
-static int
-imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+static int imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int trans, struct fb_info *info)
{
struct imxfb_info *fbi = info->par;
@@ -148,11 +268,10 @@ imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
* yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
* bitfields, horizontal timing, vertical timing.
*/
-static int
-imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+static int imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
struct imxfb_info *fbi = info->par;
- int rgbidx;
+ struct imxfb_rgb *rgb;
if (var->xres < MIN_XRES)
var->xres = MIN_XRES;
@@ -168,23 +287,25 @@ imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel);
switch (var->bits_per_pixel) {
case 16:
- rgbidx = RGB_16;
+ default:
+ if (readl(fbi->regs + LCDC_PCR) & PCR_TFT)
+ rgb = &def_rgb_16_tft;
+ else
+ rgb = &def_rgb_16_stn;
break;
case 8:
- rgbidx = RGB_8;
+ rgb = &def_rgb_8;
break;
- default:
- rgbidx = RGB_16;
}
/*
* Copy the RGB parameters for this display
* from the machine specific parameters.
*/
- var->red = fbi->rgb[rgbidx]->red;
- var->green = fbi->rgb[rgbidx]->green;
- var->blue = fbi->rgb[rgbidx]->blue;
- var->transp = fbi->rgb[rgbidx]->transp;
+ var->red = rgb->red;
+ var->green = rgb->green;
+ var->blue = rgb->blue;
+ var->transp = rgb->transp;
pr_debug("RGBT length = %d:%d:%d:%d\n",
var->red.length, var->green.length, var->blue.length,
@@ -221,8 +342,7 @@ static int imxfb_set_par(struct fb_info *info)
info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
}
- info->fix.line_length = var->xres_virtual *
- var->bits_per_pixel / 8;
+ info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16;
imxfb_activate_var(var, info);
@@ -235,22 +355,27 @@ static void imxfb_enable_controller(struct imxfb_info *fbi)
pr_debug("Enabling LCD controller\n");
/* initialize LCDC */
- LCDC_RMCR &= ~RMCR_LCDC_EN; /* just to be safe... */
+ writel(readl(fbi->regs + LCDC_RMCR) & ~RMCR_LCDC_EN,
+ fbi->regs + LCDC_RMCR); /* just to be safe... */
+
+ writel(fbi->screen_dma, fbi->regs + LCDC_SSA);
- LCDC_SSA = fbi->screen_dma;
/* physical screen start address */
- LCDC_VPW = VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4);
+ writel(VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4),
+ fbi->regs + LCDC_VPW);
- LCDC_POS = 0x00000000; /* panning offset 0 (0 pixel offset) */
+ /* panning offset 0 (0 pixel offset) */
+ writel(0x00000000, fbi->regs + LCDC_POS);
/* disable hardware cursor */
- LCDC_CPOS &= ~(CPOS_CC0 | CPOS_CC1);
+ writel(readl(fbi->regs + LCDC_CPOS) & ~(CPOS_CC0 | CPOS_CC1),
+ fbi->regs + LCDC_CPOS);
- LCDC_RMCR = RMCR_LCDC_EN;
+ writel(RMCR_LCDC_EN, fbi->regs + LCDC_RMCR);
- if(fbi->backlight_power)
+ if (fbi->backlight_power)
fbi->backlight_power(1);
- if(fbi->lcd_power)
+ if (fbi->lcd_power)
fbi->lcd_power(1);
}
@@ -258,12 +383,12 @@ static void imxfb_disable_controller(struct imxfb_info *fbi)
{
pr_debug("Disabling LCD controller\n");
- if(fbi->backlight_power)
+ if (fbi->backlight_power)
fbi->backlight_power(0);
- if(fbi->lcd_power)
+ if (fbi->lcd_power)
fbi->lcd_power(0);
- LCDC_RMCR = 0;
+ writel(0, fbi->regs + LCDC_RMCR);
}
static int imxfb_blank(int blank, struct fb_info *info)
@@ -340,74 +465,26 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
info->fix.id, var->lower_margin);
#endif
- LCDC_HCR = HCR_H_WIDTH(var->hsync_len) |
- HCR_H_WAIT_1(var->left_margin) |
- HCR_H_WAIT_2(var->right_margin);
+ writel(HCR_H_WIDTH(var->hsync_len) |
+ HCR_H_WAIT_1(var->right_margin) |
+ HCR_H_WAIT_2(var->left_margin),
+ fbi->regs + LCDC_HCR);
- LCDC_VCR = VCR_V_WIDTH(var->vsync_len) |
- VCR_V_WAIT_1(var->upper_margin) |
- VCR_V_WAIT_2(var->lower_margin);
+ writel(VCR_V_WIDTH(var->vsync_len) |
+ VCR_V_WAIT_1(var->lower_margin) |
+ VCR_V_WAIT_2(var->upper_margin),
+ fbi->regs + LCDC_VCR);
- LCDC_SIZE = SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres);
- LCDC_PCR = fbi->pcr;
- LCDC_PWMR = fbi->pwmr;
- LCDC_LSCR1 = fbi->lscr1;
- LCDC_DMACR = fbi->dmacr;
+ writel(SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres),
+ fbi->regs + LCDC_SIZE);
+ writel(fbi->pcr, fbi->regs + LCDC_PCR);
+ writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+ writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
+ writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
return 0;
}
-static void imxfb_setup_gpio(struct imxfb_info *fbi)
-{
- int width;
-
- LCDC_RMCR &= ~(RMCR_LCDC_EN | RMCR_SELF_REF);
-
- if( fbi->pcr & PCR_TFT )
- width = 16;
- else
- width = 1 << ((fbi->pcr >> 28) & 0x3);
-
- switch(width) {
- case 16:
- imx_gpio_mode(PD30_PF_LD15);
- imx_gpio_mode(PD29_PF_LD14);
- imx_gpio_mode(PD28_PF_LD13);
- imx_gpio_mode(PD27_PF_LD12);
- imx_gpio_mode(PD26_PF_LD11);
- imx_gpio_mode(PD25_PF_LD10);
- imx_gpio_mode(PD24_PF_LD9);
- imx_gpio_mode(PD23_PF_LD8);
- case 8:
- imx_gpio_mode(PD22_PF_LD7);
- imx_gpio_mode(PD21_PF_LD6);
- imx_gpio_mode(PD20_PF_LD5);
- imx_gpio_mode(PD19_PF_LD4);
- case 4:
- imx_gpio_mode(PD18_PF_LD3);
- imx_gpio_mode(PD17_PF_LD2);
- case 2:
- imx_gpio_mode(PD16_PF_LD1);
- case 1:
- imx_gpio_mode(PD15_PF_LD0);
- }
-
- /* initialize GPIOs */
- imx_gpio_mode(PD6_PF_LSCLK);
- imx_gpio_mode(PD11_PF_CONTRAST);
- imx_gpio_mode(PD14_PF_FLM_VSYNC);
- imx_gpio_mode(PD13_PF_LP_HSYNC);
- imx_gpio_mode(PD12_PF_ACD_OE);
-
- /* These are only needed for Sharp HR TFT displays */
- if (fbi->pcr & PCR_SHARP) {
- imx_gpio_mode(PD7_PF_REV);
- imx_gpio_mode(PD8_PF_CLS);
- imx_gpio_mode(PD9_PF_PS);
- imx_gpio_mode(PD10_PF_SPL_SPR);
- }
-}
-
#ifdef CONFIG_PM
/*
* Power management hooks. Note that we won't be called from IRQ context,
@@ -416,7 +493,8 @@ static void imxfb_setup_gpio(struct imxfb_info *fbi)
static int imxfb_suspend(struct platform_device *dev, pm_message_t state)
{
struct imxfb_info *fbi = platform_get_drvdata(dev);
- pr_debug("%s\n",__func__);
+
+ pr_debug("%s\n", __func__);
imxfb_disable_controller(fbi);
return 0;
@@ -425,7 +503,8 @@ static int imxfb_suspend(struct platform_device *dev, pm_message_t state)
static int imxfb_resume(struct platform_device *dev)
{
struct imxfb_info *fbi = platform_get_drvdata(dev);
- pr_debug("%s\n",__func__);
+
+ pr_debug("%s\n", __func__);
imxfb_enable_controller(fbi);
return 0;
@@ -435,149 +514,136 @@ static int imxfb_resume(struct platform_device *dev)
#define imxfb_resume NULL
#endif
-static int __init imxfb_init_fbinfo(struct device *dev)
+static int __init imxfb_init_fbinfo(struct platform_device *pdev)
{
- struct imxfb_mach_info *inf = dev->platform_data;
- struct fb_info *info = dev_get_drvdata(dev);
+ struct imx_fb_platform_data *pdata = pdev->dev.platform_data;
+ struct fb_info *info = dev_get_drvdata(&pdev->dev);
struct imxfb_info *fbi = info->par;
pr_debug("%s\n",__func__);
- info->pseudo_palette = kmalloc( sizeof(u32) * 16, GFP_KERNEL);
+ info->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
if (!info->pseudo_palette)
return -ENOMEM;
memset(fbi, 0, sizeof(struct imxfb_info));
- fbi->dev = dev;
strlcpy(info->fix.id, IMX_NAME, sizeof(info->fix.id));
- info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.type_aux = 0;
info->fix.xpanstep = 0;
info->fix.ypanstep = 0;
info->fix.ywrapstep = 0;
- info->fix.accel = FB_ACCEL_NONE;
+ info->fix.accel = FB_ACCEL_NONE;
info->var.nonstd = 0;
info->var.activate = FB_ACTIVATE_NOW;
info->var.height = -1;
info->var.width = -1;
info->var.accel_flags = 0;
- info->var.vmode = FB_VMODE_NONINTERLACED;
+ info->var.vmode = FB_VMODE_NONINTERLACED;
info->fbops = &imxfb_ops;
- info->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST;
-
- fbi->rgb[RGB_16] = &def_rgb_16;
- fbi->rgb[RGB_8] = &def_rgb_8;
-
- fbi->max_xres = inf->xres;
- info->var.xres = inf->xres;
- info->var.xres_virtual = inf->xres;
- fbi->max_yres = inf->yres;
- info->var.yres = inf->yres;
- info->var.yres_virtual = inf->yres;
- fbi->max_bpp = inf->bpp;
- info->var.bits_per_pixel = inf->bpp;
- info->var.nonstd = inf->nonstd;
- info->var.pixclock = inf->pixclock;
- info->var.hsync_len = inf->hsync_len;
- info->var.left_margin = inf->left_margin;
- info->var.right_margin = inf->right_margin;
- info->var.vsync_len = inf->vsync_len;
- info->var.upper_margin = inf->upper_margin;
- info->var.lower_margin = inf->lower_margin;
- info->var.sync = inf->sync;
- info->var.grayscale = inf->cmap_greyscale;
- fbi->cmap_inverse = inf->cmap_inverse;
- fbi->cmap_static = inf->cmap_static;
- fbi->pcr = inf->pcr;
- fbi->lscr1 = inf->lscr1;
- fbi->dmacr = inf->dmacr;
- fbi->pwmr = inf->pwmr;
- fbi->lcd_power = inf->lcd_power;
- fbi->backlight_power = inf->backlight_power;
+ info->flags = FBINFO_FLAG_DEFAULT |
+ FBINFO_READS_FAST;
+
+ fbi->max_xres = pdata->xres;
+ info->var.xres = pdata->xres;
+ info->var.xres_virtual = pdata->xres;
+ fbi->max_yres = pdata->yres;
+ info->var.yres = pdata->yres;
+ info->var.yres_virtual = pdata->yres;
+ fbi->max_bpp = pdata->bpp;
+ info->var.bits_per_pixel = pdata->bpp;
+ info->var.nonstd = pdata->nonstd;
+ info->var.pixclock = pdata->pixclock;
+ info->var.hsync_len = pdata->hsync_len;
+ info->var.left_margin = pdata->left_margin;
+ info->var.right_margin = pdata->right_margin;
+ info->var.vsync_len = pdata->vsync_len;
+ info->var.upper_margin = pdata->upper_margin;
+ info->var.lower_margin = pdata->lower_margin;
+ info->var.sync = pdata->sync;
+ info->var.grayscale = pdata->cmap_greyscale;
+ fbi->cmap_inverse = pdata->cmap_inverse;
+ fbi->cmap_static = pdata->cmap_static;
+ fbi->pcr = pdata->pcr;
+ fbi->lscr1 = pdata->lscr1;
+ fbi->dmacr = pdata->dmacr;
+ fbi->pwmr = pdata->pwmr;
+ fbi->lcd_power = pdata->lcd_power;
+ fbi->backlight_power = pdata->backlight_power;
info->fix.smem_len = fbi->max_xres * fbi->max_yres *
fbi->max_bpp / 8;
return 0;
}
-/*
- * Allocates the DRAM memory for the frame buffer. This buffer is
- * remapped into a non-cached, non-buffered, memory region to
- * allow pixel writes to occur without flushing the cache.
- * Once this area is remapped, all virtual memory access to the
- * video memory should occur at the new region.
- */
-static int __init imxfb_map_video_memory(struct fb_info *info)
-{
- struct imxfb_info *fbi = info->par;
-
- fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
- fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size,
- &fbi->map_dma,GFP_KERNEL);
-
- if (fbi->map_cpu) {
- info->screen_base = fbi->map_cpu;
- fbi->screen_cpu = fbi->map_cpu;
- fbi->screen_dma = fbi->map_dma;
- info->fix.smem_start = fbi->screen_dma;
- }
-
- return fbi->map_cpu ? 0 : -ENOMEM;
-}
-
static int __init imxfb_probe(struct platform_device *pdev)
{
struct imxfb_info *fbi;
struct fb_info *info;
- struct imxfb_mach_info *inf;
+ struct imx_fb_platform_data *pdata;
struct resource *res;
int ret;
printk("i.MX Framebuffer driver\n");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if(!res)
+ if (!res)
return -ENODEV;
- inf = pdev->dev.platform_data;
- if(!inf) {
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
dev_err(&pdev->dev,"No platform_data available\n");
return -ENOMEM;
}
info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev);
- if(!info)
+ if (!info)
return -ENOMEM;
fbi = info->par;
platform_set_drvdata(pdev, info);
- ret = imxfb_init_fbinfo(&pdev->dev);
- if( ret < 0 )
+ ret = imxfb_init_fbinfo(pdev);
+ if (ret < 0)
goto failed_init;
- res = request_mem_region(res->start, res->end - res->start + 1, "IMXFB");
+ res = request_mem_region(res->start, resource_size(res),
+ DRIVER_NAME);
if (!res) {
ret = -EBUSY;
- goto failed_regs;
+ goto failed_req;
+ }
+
+ fbi->regs = ioremap(res->start, resource_size(res));
+ if (fbi->regs == NULL) {
+ printk(KERN_ERR"Cannot map frame buffer registers\n");
+ goto failed_ioremap;
}
- if (!inf->fixed_screen_cpu) {
- ret = imxfb_map_video_memory(info);
- if (ret) {
+ if (!pdata->fixed_screen_cpu) {
+ fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
+ fbi->map_cpu = dma_alloc_writecombine(&pdev->dev,
+ fbi->map_size, &fbi->map_dma, GFP_KERNEL);
+
+ if (!fbi->map_cpu) {
dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto failed_map;
}
+
+ info->screen_base = fbi->map_cpu;
+ fbi->screen_cpu = fbi->map_cpu;
+ fbi->screen_dma = fbi->map_dma;
+ info->fix.smem_start = fbi->screen_dma;
} else {
/* Fixed framebuffer mapping enables location of the screen in eSRAM */
- fbi->map_cpu = inf->fixed_screen_cpu;
- fbi->map_dma = inf->fixed_screen_dma;
+ fbi->map_cpu = pdata->fixed_screen_cpu;
+ fbi->map_dma = pdata->fixed_screen_dma;
info->screen_base = fbi->map_cpu;
fbi->screen_cpu = fbi->map_cpu;
fbi->screen_dma = fbi->map_dma;
@@ -590,12 +656,10 @@ static int __init imxfb_probe(struct platform_device *pdev)
*/
imxfb_check_var(&info->var, info);
- ret = fb_alloc_cmap(&info->cmap, 1<<info->var.bits_per_pixel, 0);
+ ret = fb_alloc_cmap(&info->cmap, 1 << info->var.bits_per_pixel, 0);
if (ret < 0)
goto failed_cmap;
- imxfb_setup_gpio(fbi);
-
imxfb_set_par(info);
ret = register_framebuffer(info);
if (ret < 0) {
@@ -610,20 +674,22 @@ static int __init imxfb_probe(struct platform_device *pdev)
failed_register:
fb_dealloc_cmap(&info->cmap);
failed_cmap:
- if (!inf->fixed_screen_cpu)
+ if (!pdata->fixed_screen_cpu)
dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu,
- fbi->map_dma);
+ fbi->map_dma);
failed_map:
- kfree(info->pseudo_palette);
-failed_regs:
+ iounmap(fbi->regs);
+failed_ioremap:
release_mem_region(res->start, res->end - res->start);
+failed_req:
+ kfree(info->pseudo_palette);
failed_init:
platform_set_drvdata(pdev, NULL);
framebuffer_release(info);
return ret;
}
-static int imxfb_remove(struct platform_device *pdev)
+static int __devexit imxfb_remove(struct platform_device *pdev)
{
struct fb_info *info = platform_get_drvdata(pdev);
struct imxfb_info *fbi = info->par;
@@ -639,6 +705,7 @@ static int imxfb_remove(struct platform_device *pdev)
kfree(info->pseudo_palette);
framebuffer_release(info);
+ iounmap(fbi->regs);
release_mem_region(res->start, res->end - res->start + 1);
platform_set_drvdata(pdev, NULL);
@@ -653,19 +720,18 @@ void imxfb_shutdown(struct platform_device * dev)
}
static struct platform_driver imxfb_driver = {
- .probe = imxfb_probe,
.suspend = imxfb_suspend,
.resume = imxfb_resume,
- .remove = imxfb_remove,
+ .remove = __devexit_p(imxfb_remove),
.shutdown = imxfb_shutdown,
.driver = {
- .name = "imx-fb",
+ .name = DRIVER_NAME,
},
};
int __init imxfb_init(void)
{
- return platform_driver_register(&imxfb_driver);
+ return platform_driver_probe(&imxfb_driver, imxfb_probe);
}
static void __exit imxfb_cleanup(void)
diff --git a/drivers/video/imxfb.h b/drivers/video/imxfb.h
deleted file mode 100644
index e837a8b48eb8..000000000000
--- a/drivers/video/imxfb.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * linux/drivers/video/imxfb.h
- *
- * Freescale i.MX Frame Buffer device driver
- *
- * Copyright (C) 2004 S.Hauer, Pengutronix
- *
- * Copyright (C) 1999 Eric A. Thomas
- * Based on acornfb.c Copyright (C) Russell King.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- */
-
-/*
- * These are the bitfields for each
- * display depth that we support.
- */
-struct imxfb_rgb {
- struct fb_bitfield red;
- struct fb_bitfield green;
- struct fb_bitfield blue;
- struct fb_bitfield transp;
-};
-
-#define RGB_16 (0)
-#define RGB_8 (1)
-#define NR_RGB 2
-
-struct imxfb_info {
- struct device *dev;
- struct imxfb_rgb *rgb[NR_RGB];
-
- u_int max_bpp;
- u_int max_xres;
- u_int max_yres;
-
- /*
- * These are the addresses we mapped
- * the framebuffer memory region to.
- */
- dma_addr_t map_dma;
- u_char * map_cpu;
- u_int map_size;
-
- u_char * screen_cpu;
- dma_addr_t screen_dma;
- u_int palette_size;
-
- dma_addr_t dbar1;
- dma_addr_t dbar2;
-
- u_int pcr;
- u_int pwmr;
- u_int lscr1;
- u_int dmacr;
- u_int cmap_inverse:1,
- cmap_static:1,
- unused:30;
-
- void (*lcd_power)(int);
- void (*backlight_power)(int);
-};
-
-#define IMX_NAME "IMX"
-
-/*
- * Minimum X and Y resolutions
- */
-#define MIN_XRES 64
-#define MIN_YRES 64
-
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index cc59c52e1103..48ff701d3a72 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -20,6 +20,16 @@
*
* linux-arm-kernel@lists.arm.linux.org.uk
*
+ * Add support for overlay1 and overlay2 based on pxafb_overlay.c:
+ *
+ * Copyright (C) 2004, Intel Corporation
+ *
+ * 2003/08/27: <yu.tang@intel.com>
+ * 2004/03/10: <stanley.cai@intel.com>
+ * 2004/10/28: <yan.yin@intel.com>
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * All Rights Reserved
*/
#include <linux/module.h>
@@ -50,7 +60,6 @@
#include <asm/irq.h>
#include <asm/div64.h>
#include <mach/pxa-regs.h>
-#include <mach/pxa2xx-gpio.h>
#include <mach/bitfield.h>
#include <mach/pxafb.h>
@@ -67,14 +76,16 @@
LCCR0_SFM | LCCR0_LDM | LCCR0_ENB)
#define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP | LCCR3_VSP |\
- LCCR3_PCD | LCCR3_BPP)
-
-static void (*pxafb_backlight_power)(int);
-static void (*pxafb_lcd_power)(int, struct fb_var_screeninfo *);
+ LCCR3_PCD | LCCR3_BPP(0xf))
static int pxafb_activate_var(struct fb_var_screeninfo *var,
struct pxafb_info *);
static void set_ctrlr_state(struct pxafb_info *fbi, u_int state);
+static void setup_base_frame(struct pxafb_info *fbi, int branch);
+static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,
+ unsigned long offset, size_t size);
+
+static unsigned long video_mem_size = 0;
static inline unsigned long
lcd_readl(struct pxafb_info *fbi, unsigned int off)
@@ -156,6 +167,12 @@ pxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
val |= ((blue >> 8) & 0x000000fc);
((u32 *)(fbi->palette_cpu))[regno] = val;
break;
+ case LCCR4_PAL_FOR_3:
+ val = ((red << 8) & 0x00ff0000);
+ val |= ((green >> 0) & 0x0000ff00);
+ val |= ((blue >> 8) & 0x000000ff);
+ ((u32 *)(fbi->palette_cpu))[regno] = val;
+ break;
}
return 0;
@@ -216,37 +233,110 @@ pxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
return ret;
}
-/*
- * pxafb_bpp_to_lccr3():
- * Convert a bits per pixel value to the correct bit pattern for LCCR3
- */
-static int pxafb_bpp_to_lccr3(struct fb_var_screeninfo *var)
+/* calculate pixel depth, transparency bit included, >=16bpp formats _only_ */
+static inline int var_to_depth(struct fb_var_screeninfo *var)
{
- int ret = 0;
+ return var->red.length + var->green.length +
+ var->blue.length + var->transp.length;
+}
+
+/* calculate 4-bit BPP value for LCCR3 and OVLxC1 */
+static int pxafb_var_to_bpp(struct fb_var_screeninfo *var)
+{
+ int bpp = -EINVAL;
+
switch (var->bits_per_pixel) {
- case 1: ret = LCCR3_1BPP; break;
- case 2: ret = LCCR3_2BPP; break;
- case 4: ret = LCCR3_4BPP; break;
- case 8: ret = LCCR3_8BPP; break;
- case 16: ret = LCCR3_16BPP; break;
+ case 1: bpp = 0; break;
+ case 2: bpp = 1; break;
+ case 4: bpp = 2; break;
+ case 8: bpp = 3; break;
+ case 16: bpp = 4; break;
case 24:
- switch (var->red.length + var->green.length +
- var->blue.length + var->transp.length) {
- case 18: ret = LCCR3_18BPP_P | LCCR3_PDFOR_3; break;
- case 19: ret = LCCR3_19BPP_P; break;
+ switch (var_to_depth(var)) {
+ case 18: bpp = 6; break; /* 18-bits/pixel packed */
+ case 19: bpp = 8; break; /* 19-bits/pixel packed */
+ case 24: bpp = 9; break;
}
break;
case 32:
- switch (var->red.length + var->green.length +
- var->blue.length + var->transp.length) {
- case 18: ret = LCCR3_18BPP | LCCR3_PDFOR_3; break;
- case 19: ret = LCCR3_19BPP; break;
- case 24: ret = LCCR3_24BPP | LCCR3_PDFOR_3; break;
- case 25: ret = LCCR3_25BPP; break;
+ switch (var_to_depth(var)) {
+ case 18: bpp = 5; break; /* 18-bits/pixel unpacked */
+ case 19: bpp = 7; break; /* 19-bits/pixel unpacked */
+ case 25: bpp = 10; break;
}
break;
}
- return ret;
+ return bpp;
+}
+
+/*
+ * pxafb_var_to_lccr3():
+ * Convert a bits per pixel value to the correct bit pattern for LCCR3
+ *
+ * NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an
+ * implication of the acutal use of transparency bit, which we handle it
+ * here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel
+ * Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP.
+ *
+ * Transparency for palette pixel formats is not supported at the moment.
+ */
+static uint32_t pxafb_var_to_lccr3(struct fb_var_screeninfo *var)
+{
+ int bpp = pxafb_var_to_bpp(var);
+ uint32_t lccr3;
+
+ if (bpp < 0)
+ return 0;
+
+ lccr3 = LCCR3_BPP(bpp);
+
+ switch (var_to_depth(var)) {
+ case 16: lccr3 |= var->transp.length ? LCCR3_PDFOR_3 : 0; break;
+ case 18: lccr3 |= LCCR3_PDFOR_3; break;
+ case 24: lccr3 |= var->transp.length ? LCCR3_PDFOR_2 : LCCR3_PDFOR_3;
+ break;
+ case 19:
+ case 25: lccr3 |= LCCR3_PDFOR_0; break;
+ }
+ return lccr3;
+}
+
+#define SET_PIXFMT(v, r, g, b, t) \
+({ \
+ (v)->transp.offset = (t) ? (r) + (g) + (b) : 0; \
+ (v)->transp.length = (t) ? (t) : 0; \
+ (v)->blue.length = (b); (v)->blue.offset = 0; \
+ (v)->green.length = (g); (v)->green.offset = (b); \
+ (v)->red.length = (r); (v)->red.offset = (b) + (g); \
+})
+
+/* set the RGBT bitfields of fb_var_screeninf according to
+ * var->bits_per_pixel and given depth
+ */
+static void pxafb_set_pixfmt(struct fb_var_screeninfo *var, int depth)
+{
+ if (depth == 0)
+ depth = var->bits_per_pixel;
+
+ if (var->bits_per_pixel < 16) {
+ /* indexed pixel formats */
+ var->red.offset = 0; var->red.length = 8;
+ var->green.offset = 0; var->green.length = 8;
+ var->blue.offset = 0; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 8;
+ }
+
+ switch (depth) {
+ case 16: var->transp.length ?
+ SET_PIXFMT(var, 5, 5, 5, 1) : /* RGBT555 */
+ SET_PIXFMT(var, 5, 6, 5, 0); break; /* RGB565 */
+ case 18: SET_PIXFMT(var, 6, 6, 6, 0); break; /* RGB666 */
+ case 19: SET_PIXFMT(var, 6, 6, 6, 1); break; /* RGBT666 */
+ case 24: var->transp.length ?
+ SET_PIXFMT(var, 8, 8, 7, 1) : /* RGBT887 */
+ SET_PIXFMT(var, 8, 8, 8, 0); break; /* RGB888 */
+ case 25: SET_PIXFMT(var, 8, 8, 8, 1); break; /* RGBT888 */
+ }
}
#ifdef CONFIG_CPU_FREQ
@@ -308,8 +398,49 @@ static void pxafb_setmode(struct fb_var_screeninfo *var,
var->lower_margin = mode->lower_margin;
var->sync = mode->sync;
var->grayscale = mode->cmap_greyscale;
- var->xres_virtual = var->xres;
- var->yres_virtual = var->yres;
+
+ /* set the initial RGBA bitfields */
+ pxafb_set_pixfmt(var, mode->depth);
+}
+
+static int pxafb_adjust_timing(struct pxafb_info *fbi,
+ struct fb_var_screeninfo *var)
+{
+ int line_length;
+
+ var->xres = max_t(int, var->xres, MIN_XRES);
+ var->yres = max_t(int, var->yres, MIN_YRES);
+
+ if (!(fbi->lccr0 & LCCR0_LCDT)) {
+ clamp_val(var->hsync_len, 1, 64);
+ clamp_val(var->vsync_len, 1, 64);
+ clamp_val(var->left_margin, 1, 255);
+ clamp_val(var->right_margin, 1, 255);
+ clamp_val(var->upper_margin, 1, 255);
+ clamp_val(var->lower_margin, 1, 255);
+ }
+
+ /* make sure each line is aligned on word boundary */
+ line_length = var->xres * var->bits_per_pixel / 8;
+ line_length = ALIGN(line_length, 4);
+ var->xres = line_length * 8 / var->bits_per_pixel;
+
+ /* we don't support xpan, force xres_virtual to be equal to xres */
+ var->xres_virtual = var->xres;
+
+ if (var->accel_flags & FB_ACCELF_TEXT)
+ var->yres_virtual = fbi->fb.fix.smem_len / line_length;
+ else
+ var->yres_virtual = max(var->yres_virtual, var->yres);
+
+ /* check for limits */
+ if (var->xres > MAX_XRES || var->yres > MAX_YRES)
+ return -EINVAL;
+
+ if (var->yres > var->yres_virtual)
+ return -EINVAL;
+
+ return 0;
}
/*
@@ -325,11 +456,7 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
struct pxafb_info *fbi = (struct pxafb_info *)info;
struct pxafb_mach_info *inf = fbi->dev->platform_data;
-
- if (var->xres < MIN_XRES)
- var->xres = MIN_XRES;
- if (var->yres < MIN_YRES)
- var->yres = MIN_YRES;
+ int err;
if (inf->fixed_modes) {
struct pxafb_mode_info *mode;
@@ -338,74 +465,18 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
if (!mode)
return -EINVAL;
pxafb_setmode(var, mode);
- } else {
- if (var->xres > inf->modes->xres)
- return -EINVAL;
- if (var->yres > inf->modes->yres)
- return -EINVAL;
- if (var->bits_per_pixel > inf->modes->bpp)
- return -EINVAL;
}
- var->xres_virtual =
- max(var->xres_virtual, var->xres);
- var->yres_virtual =
- max(var->yres_virtual, var->yres);
+ /* do a test conversion to BPP fields to check the color formats */
+ err = pxafb_var_to_bpp(var);
+ if (err < 0)
+ return err;
- /*
- * Setup the RGB parameters for this display.
- *
- * The pixel packing format is described on page 7-11 of the
- * PXA2XX Developer's Manual.
- */
- if (var->bits_per_pixel == 16) {
- var->red.offset = 11; var->red.length = 5;
- var->green.offset = 5; var->green.length = 6;
- var->blue.offset = 0; var->blue.length = 5;
- var->transp.offset = var->transp.length = 0;
- } else if (var->bits_per_pixel > 16) {
- struct pxafb_mode_info *mode;
+ pxafb_set_pixfmt(var, var_to_depth(var));
- mode = pxafb_getmode(inf, var);
- if (!mode)
- return -EINVAL;
-
- switch (mode->depth) {
- case 18: /* RGB666 */
- var->transp.offset = var->transp.length = 0;
- var->red.offset = 12; var->red.length = 6;
- var->green.offset = 6; var->green.length = 6;
- var->blue.offset = 0; var->blue.length = 6;
- break;
- case 19: /* RGBT666 */
- var->transp.offset = 18; var->transp.length = 1;
- var->red.offset = 12; var->red.length = 6;
- var->green.offset = 6; var->green.length = 6;
- var->blue.offset = 0; var->blue.length = 6;
- break;
- case 24: /* RGB888 */
- var->transp.offset = var->transp.length = 0;
- var->red.offset = 16; var->red.length = 8;
- var->green.offset = 8; var->green.length = 8;
- var->blue.offset = 0; var->blue.length = 8;
- break;
- case 25: /* RGBT888 */
- var->transp.offset = 24; var->transp.length = 1;
- var->red.offset = 16; var->red.length = 8;
- var->green.offset = 8; var->green.length = 8;
- var->blue.offset = 0; var->blue.length = 8;
- break;
- default:
- return -EINVAL;
- }
- } else {
- var->red.offset = var->green.offset = 0;
- var->blue.offset = var->transp.offset = 0;
- var->red.length = 8;
- var->green.length = 8;
- var->blue.length = 8;
- var->transp.length = 0;
- }
+ err = pxafb_adjust_timing(fbi, var);
+ if (err)
+ return err;
#ifdef CONFIG_CPU_FREQ
pr_debug("pxafb: dma period = %d ps\n",
@@ -415,11 +486,6 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
return 0;
}
-static inline void pxafb_set_truecolor(u_int is_true_color)
-{
- /* do your machine-specific setup if needed */
-}
-
/*
* pxafb_set_par():
* Set the user defined part of the display for the specified console
@@ -452,11 +518,6 @@ static int pxafb_set_par(struct fb_info *info)
fbi->palette_cpu = (u16 *)&fbi->dma_buff->palette[0];
- /*
- * Set (any) board control register to handle new color depth
- */
- pxafb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR);
-
if (fbi->fb.var.bits_per_pixel >= 16)
fb_dealloc_cmap(&fbi->fb.cmap);
else
@@ -467,6 +528,24 @@ static int pxafb_set_par(struct fb_info *info)
return 0;
}
+static int pxafb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct pxafb_info *fbi = (struct pxafb_info *)info;
+ int dma = DMA_MAX + DMA_BASE;
+
+ if (fbi->state != C_ENABLE)
+ return 0;
+
+ setup_base_frame(fbi, 1);
+
+ if (fbi->lccr0 & LCCR0_SDS)
+ lcd_writel(fbi, FBR1, fbi->fdadr[dma + 1] | 0x1);
+
+ lcd_writel(fbi, FBR0, fbi->fdadr[dma] | 0x1);
+ return 0;
+}
+
/*
* pxafb_blank():
* Blank the display by setting all palette values to zero. Note, the
@@ -502,32 +581,342 @@ static int pxafb_blank(int blank, struct fb_info *info)
return 0;
}
-static int pxafb_mmap(struct fb_info *info,
- struct vm_area_struct *vma)
-{
- struct pxafb_info *fbi = (struct pxafb_info *)info;
- unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
-
- if (off < info->fix.smem_len) {
- vma->vm_pgoff += fbi->video_offset / PAGE_SIZE;
- return dma_mmap_writecombine(fbi->dev, vma, fbi->map_cpu,
- fbi->map_dma, fbi->map_size);
- }
- return -EINVAL;
-}
-
static struct fb_ops pxafb_ops = {
.owner = THIS_MODULE,
.fb_check_var = pxafb_check_var,
.fb_set_par = pxafb_set_par,
+ .fb_pan_display = pxafb_pan_display,
.fb_setcolreg = pxafb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_blank = pxafb_blank,
- .fb_mmap = pxafb_mmap,
};
+#ifdef CONFIG_FB_PXA_OVERLAY
+static void overlay1fb_setup(struct pxafb_layer *ofb)
+{
+ int size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual;
+ unsigned long start = ofb->video_mem_phys;
+ setup_frame_dma(ofb->fbi, DMA_OV1, PAL_NONE, start, size);
+}
+
+/* Depending on the enable status of overlay1/2, the DMA should be
+ * updated from FDADRx (when disabled) or FBRx (when enabled).
+ */
+static void overlay1fb_enable(struct pxafb_layer *ofb)
+{
+ int enabled = lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN;
+ uint32_t fdadr1 = ofb->fbi->fdadr[DMA_OV1] | (enabled ? 0x1 : 0);
+
+ lcd_writel(ofb->fbi, enabled ? FBR1 : FDADR1, fdadr1);
+ lcd_writel(ofb->fbi, OVL1C2, ofb->control[1]);
+ lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] | OVLxC1_OEN);
+}
+
+static void overlay1fb_disable(struct pxafb_layer *ofb)
+{
+ uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5);
+
+ lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] & ~OVLxC1_OEN);
+
+ lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(1));
+ lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(1));
+ lcd_writel(ofb->fbi, FBR1, ofb->fbi->fdadr[DMA_OV1] | 0x3);
+
+ if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0)
+ pr_warning("%s: timeout disabling overlay1\n", __func__);
+
+ lcd_writel(ofb->fbi, LCCR5, lccr5);
+}
+
+static void overlay2fb_setup(struct pxafb_layer *ofb)
+{
+ int size, div = 1, pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd);
+ unsigned long start[3] = { ofb->video_mem_phys, 0, 0 };
+
+ if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) {
+ size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual;
+ setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size);
+ } else {
+ size = ofb->fb.var.xres_virtual * ofb->fb.var.yres_virtual;
+ switch (pfor) {
+ case OVERLAY_FORMAT_YUV444_PLANAR: div = 1; break;
+ case OVERLAY_FORMAT_YUV422_PLANAR: div = 2; break;
+ case OVERLAY_FORMAT_YUV420_PLANAR: div = 4; break;
+ }
+ start[1] = start[0] + size;
+ start[2] = start[1] + size / div;
+ setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size);
+ setup_frame_dma(ofb->fbi, DMA_OV2_Cb, -1, start[1], size / div);
+ setup_frame_dma(ofb->fbi, DMA_OV2_Cr, -1, start[2], size / div);
+ }
+}
+
+static void overlay2fb_enable(struct pxafb_layer *ofb)
+{
+ int pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd);
+ int enabled = lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN;
+ uint32_t fdadr2 = ofb->fbi->fdadr[DMA_OV2_Y] | (enabled ? 0x1 : 0);
+ uint32_t fdadr3 = ofb->fbi->fdadr[DMA_OV2_Cb] | (enabled ? 0x1 : 0);
+ uint32_t fdadr4 = ofb->fbi->fdadr[DMA_OV2_Cr] | (enabled ? 0x1 : 0);
+
+ if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED)
+ lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2);
+ else {
+ lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2);
+ lcd_writel(ofb->fbi, enabled ? FBR3 : FDADR3, fdadr3);
+ lcd_writel(ofb->fbi, enabled ? FBR4 : FDADR4, fdadr4);
+ }
+ lcd_writel(ofb->fbi, OVL2C2, ofb->control[1]);
+ lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] | OVLxC1_OEN);
+}
+
+static void overlay2fb_disable(struct pxafb_layer *ofb)
+{
+ uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5);
+
+ lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] & ~OVLxC1_OEN);
+
+ lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(2));
+ lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(2));
+ lcd_writel(ofb->fbi, FBR2, ofb->fbi->fdadr[DMA_OV2_Y] | 0x3);
+ lcd_writel(ofb->fbi, FBR3, ofb->fbi->fdadr[DMA_OV2_Cb] | 0x3);
+ lcd_writel(ofb->fbi, FBR4, ofb->fbi->fdadr[DMA_OV2_Cr] | 0x3);
+
+ if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0)
+ pr_warning("%s: timeout disabling overlay2\n", __func__);
+}
+
+static struct pxafb_layer_ops ofb_ops[] = {
+ [0] = {
+ .enable = overlay1fb_enable,
+ .disable = overlay1fb_disable,
+ .setup = overlay1fb_setup,
+ },
+ [1] = {
+ .enable = overlay2fb_enable,
+ .disable = overlay2fb_disable,
+ .setup = overlay2fb_setup,
+ },
+};
+
+static int overlayfb_open(struct fb_info *info, int user)
+{
+ struct pxafb_layer *ofb = (struct pxafb_layer *)info;
+
+ /* no support for framebuffer console on overlay */
+ if (user == 0)
+ return -ENODEV;
+
+ /* allow only one user at a time */
+ if (atomic_inc_and_test(&ofb->usage))
+ return -EBUSY;
+
+ /* unblank the base framebuffer */
+ fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK);
+ return 0;
+}
+
+static int overlayfb_release(struct fb_info *info, int user)
+{
+ struct pxafb_layer *ofb = (struct pxafb_layer*) info;
+
+ atomic_dec(&ofb->usage);
+ ofb->ops->disable(ofb);
+
+ free_pages_exact(ofb->video_mem, ofb->video_mem_size);
+ ofb->video_mem = NULL;
+ ofb->video_mem_size = 0;
+ return 0;
+}
+
+static int overlayfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct pxafb_layer *ofb = (struct pxafb_layer *)info;
+ struct fb_var_screeninfo *base_var = &ofb->fbi->fb.var;
+ int xpos, ypos, pfor, bpp;
+
+ xpos = NONSTD_TO_XPOS(var->nonstd);
+ ypos = NONSTD_TO_XPOS(var->nonstd);
+ pfor = NONSTD_TO_PFOR(var->nonstd);
+
+ bpp = pxafb_var_to_bpp(var);
+ if (bpp < 0)
+ return -EINVAL;
+
+ /* no support for YUV format on overlay1 */
+ if (ofb->id == OVERLAY1 && pfor != 0)
+ return -EINVAL;
+
+ /* for YUV packed formats, bpp = 'minimum bpp of YUV components' */
+ switch (pfor) {
+ case OVERLAY_FORMAT_RGB:
+ bpp = pxafb_var_to_bpp(var);
+ if (bpp < 0)
+ return -EINVAL;
+
+ pxafb_set_pixfmt(var, var_to_depth(var));
+ break;
+ case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break;
+ case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 8; break;
+ case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 4; break;
+ case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 2; break;
+ default:
+ return -EINVAL;
+ }
+
+ /* each line must start at a 32-bit word boundary */
+ if ((xpos * bpp) % 32)
+ return -EINVAL;
+
+ /* xres must align on 32-bit word boundary */
+ var->xres = roundup(var->xres * bpp, 32) / bpp;
+
+ if ((xpos + var->xres > base_var->xres) ||
+ (ypos + var->yres > base_var->yres))
+ return -EINVAL;
+
+ var->xres_virtual = var->xres;
+ var->yres_virtual = max(var->yres, var->yres_virtual);
+ return 0;
+}
+
+static int overlayfb_map_video_memory(struct pxafb_layer *ofb)
+{
+ struct fb_var_screeninfo *var = &ofb->fb.var;
+ int pfor = NONSTD_TO_PFOR(var->nonstd);
+ int size, bpp = 0;
+
+ switch (pfor) {
+ case OVERLAY_FORMAT_RGB: bpp = var->bits_per_pixel; break;
+ case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break;
+ case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 24; break;
+ case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 16; break;
+ case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 12; break;
+ }
+
+ ofb->fb.fix.line_length = var->xres_virtual * bpp / 8;
+
+ size = PAGE_ALIGN(ofb->fb.fix.line_length * var->yres_virtual);
+
+ /* don't re-allocate if the original video memory is enough */
+ if (ofb->video_mem) {
+ if (ofb->video_mem_size >= size)
+ return 0;
+
+ free_pages_exact(ofb->video_mem, ofb->video_mem_size);
+ }
+
+ ofb->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
+ if (ofb->video_mem == NULL)
+ return -ENOMEM;
+
+ ofb->video_mem_phys = virt_to_phys(ofb->video_mem);
+ ofb->video_mem_size = size;
+
+ ofb->fb.fix.smem_start = ofb->video_mem_phys;
+ ofb->fb.fix.smem_len = ofb->fb.fix.line_length * var->yres_virtual;
+ ofb->fb.screen_base = ofb->video_mem;
+ return 0;
+}
+
+static int overlayfb_set_par(struct fb_info *info)
+{
+ struct pxafb_layer *ofb = (struct pxafb_layer *)info;
+ struct fb_var_screeninfo *var = &info->var;
+ int xpos, ypos, pfor, bpp, ret;
+
+ ret = overlayfb_map_video_memory(ofb);
+ if (ret)
+ return ret;
+
+ bpp = pxafb_var_to_bpp(var);
+ xpos = NONSTD_TO_XPOS(var->nonstd);
+ ypos = NONSTD_TO_XPOS(var->nonstd);
+ pfor = NONSTD_TO_PFOR(var->nonstd);
+
+ ofb->control[0] = OVLxC1_PPL(var->xres) | OVLxC1_LPO(var->yres) |
+ OVLxC1_BPP(bpp);
+ ofb->control[1] = OVLxC2_XPOS(xpos) | OVLxC2_YPOS(ypos);
+
+ if (ofb->id == OVERLAY2)
+ ofb->control[1] |= OVL2C2_PFOR(pfor);
+
+ ofb->ops->setup(ofb);
+ ofb->ops->enable(ofb);
+ return 0;
+}
+
+static struct fb_ops overlay_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = overlayfb_open,
+ .fb_release = overlayfb_release,
+ .fb_check_var = overlayfb_check_var,
+ .fb_set_par = overlayfb_set_par,
+};
+
+static void __devinit init_pxafb_overlay(struct pxafb_info *fbi,
+ struct pxafb_layer *ofb, int id)
+{
+ sprintf(ofb->fb.fix.id, "overlay%d", id + 1);
+
+ ofb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
+ ofb->fb.fix.xpanstep = 0;
+ ofb->fb.fix.ypanstep = 1;
+
+ ofb->fb.var.activate = FB_ACTIVATE_NOW;
+ ofb->fb.var.height = -1;
+ ofb->fb.var.width = -1;
+ ofb->fb.var.vmode = FB_VMODE_NONINTERLACED;
+
+ ofb->fb.fbops = &overlay_fb_ops;
+ ofb->fb.flags = FBINFO_FLAG_DEFAULT;
+ ofb->fb.node = -1;
+ ofb->fb.pseudo_palette = NULL;
+
+ ofb->id = id;
+ ofb->ops = &ofb_ops[id];
+ atomic_set(&ofb->usage, 0);
+ ofb->fbi = fbi;
+ init_completion(&ofb->branch_done);
+}
+
+static int __devinit pxafb_overlay_init(struct pxafb_info *fbi)
+{
+ int i, ret;
+
+ for (i = 0; i < 2; i++) {
+ init_pxafb_overlay(fbi, &fbi->overlay[i], i);
+ ret = register_framebuffer(&fbi->overlay[i].fb);
+ if (ret) {
+ dev_err(fbi->dev, "failed to register overlay %d\n", i);
+ return ret;
+ }
+ }
+
+ /* mask all IU/BS/EOF/SOF interrupts */
+ lcd_writel(fbi, LCCR5, ~0);
+
+ /* place overlay(s) on top of base */
+ fbi->lccr0 |= LCCR0_OUC;
+ pr_info("PXA Overlay driver loaded successfully!\n");
+ return 0;
+}
+
+static void __devexit pxafb_overlay_exit(struct pxafb_info *fbi)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ unregister_framebuffer(&fbi->overlay[i].fb);
+}
+#else
+static inline void pxafb_overlay_init(struct pxafb_info *fbi) {}
+static inline void pxafb_overlay_exit(struct pxafb_info *fbi) {}
+#endif /* CONFIG_FB_PXA_OVERLAY */
+
/*
* Calculate the PCD value from the clock rate (in picoseconds).
* We take account of the PPCR clock setting.
@@ -607,22 +996,22 @@ unsigned long pxafb_get_hsync_time(struct device *dev)
EXPORT_SYMBOL(pxafb_get_hsync_time);
static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,
- unsigned int offset, size_t size)
+ unsigned long start, size_t size)
{
struct pxafb_dma_descriptor *dma_desc, *pal_desc;
unsigned int dma_desc_off, pal_desc_off;
- if (dma < 0 || dma >= DMA_MAX)
+ if (dma < 0 || dma >= DMA_MAX * 2)
return -EINVAL;
dma_desc = &fbi->dma_buff->dma_desc[dma];
dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]);
- dma_desc->fsadr = fbi->screen_dma + offset;
+ dma_desc->fsadr = start;
dma_desc->fidr = 0;
dma_desc->ldcmd = size;
- if (pal < 0 || pal >= PAL_MAX) {
+ if (pal < 0 || pal >= PAL_MAX * 2) {
dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off;
fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off;
} else {
@@ -648,6 +1037,27 @@ static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,
return 0;
}
+static void setup_base_frame(struct pxafb_info *fbi, int branch)
+{
+ struct fb_var_screeninfo *var = &fbi->fb.var;
+ struct fb_fix_screeninfo *fix = &fbi->fb.fix;
+ int nbytes, dma, pal, bpp = var->bits_per_pixel;
+ unsigned long offset;
+
+ dma = DMA_BASE + (branch ? DMA_MAX : 0);
+ pal = (bpp >= 16) ? PAL_NONE : PAL_BASE + (branch ? PAL_MAX : 0);
+
+ nbytes = fix->line_length * var->yres;
+ offset = fix->line_length * var->yoffset + fbi->video_mem_phys;
+
+ if (fbi->lccr0 & LCCR0_SDS) {
+ nbytes = nbytes / 2;
+ setup_frame_dma(fbi, dma + 1, PAL_NONE, offset + nbytes, nbytes);
+ }
+
+ setup_frame_dma(fbi, dma, pal, offset, nbytes);
+}
+
#ifdef CONFIG_FB_PXA_SMARTPANEL
static int setup_smart_dma(struct pxafb_info *fbi)
{
@@ -701,6 +1111,7 @@ int pxafb_smart_flush(struct fb_info *info)
lcd_writel(fbi, LCCR1, fbi->reg_lccr1);
lcd_writel(fbi, LCCR2, fbi->reg_lccr2);
lcd_writel(fbi, LCCR3, fbi->reg_lccr3);
+ lcd_writel(fbi, LCCR4, fbi->reg_lccr4);
lcd_writel(fbi, FDADR0, fbi->fdadr[0]);
lcd_writel(fbi, FDADR6, fbi->fdadr[6]);
@@ -727,12 +1138,19 @@ int pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds)
int i;
struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb);
- /* leave 2 commands for INTERRUPT and WAIT_FOR_SYNC */
- for (i = 0; i < n_cmds; i++) {
+ for (i = 0; i < n_cmds; i++, cmds++) {
+ /* if it is a software delay, flush and delay */
+ if ((*cmds & 0xff00) == SMART_CMD_DELAY) {
+ pxafb_smart_flush(info);
+ mdelay(*cmds & 0xff);
+ continue;
+ }
+
+ /* leave 2 commands for INTERRUPT and WAIT_FOR_SYNC */
if (fbi->n_smart_cmds == CMD_BUFF_SIZE - 8)
pxafb_smart_flush(info);
- fbi->smart_cmds[fbi->n_smart_cmds++] = *cmds++;
+ fbi->smart_cmds[fbi->n_smart_cmds++] = *cmds;
}
return 0;
@@ -764,7 +1182,9 @@ static void setup_smart_timing(struct pxafb_info *fbi,
LCCR1_HorSnchWdth(__smart_timing(t3, lclk));
fbi->reg_lccr2 = LCCR2_DisHght(var->yres);
- fbi->reg_lccr3 = LCCR3_PixClkDiv(__smart_timing(t4, lclk));
+ fbi->reg_lccr3 = fbi->lccr3 | LCCR3_PixClkDiv(__smart_timing(t4, lclk));
+ fbi->reg_lccr3 |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? LCCR3_HSP : 0;
+ fbi->reg_lccr3 |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? LCCR3_VSP : 0;
/* FIXME: make this configurable */
fbi->reg_cmdcr = 1;
@@ -789,11 +1209,15 @@ static int pxafb_smart_thread(void *arg)
if (try_to_freeze())
continue;
+ mutex_lock(&fbi->ctrlr_lock);
+
if (fbi->state == C_ENABLE) {
inf->smart_update(&fbi->fb);
complete(&fbi->refresh_done);
}
+ mutex_unlock(&fbi->ctrlr_lock);
+
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(30 * HZ / 1000);
}
@@ -804,16 +1228,22 @@ static int pxafb_smart_thread(void *arg)
static int pxafb_smart_init(struct pxafb_info *fbi)
{
- if (!(fbi->lccr0 | LCCR0_LCDT))
+ if (!(fbi->lccr0 & LCCR0_LCDT))
return 0;
+ fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff;
+ fbi->n_smart_cmds = 0;
+
+ init_completion(&fbi->command_done);
+ init_completion(&fbi->refresh_done);
+
fbi->smart_thread = kthread_run(pxafb_smart_thread, fbi,
"lcd_refresh");
if (IS_ERR(fbi->smart_thread)) {
- printk(KERN_ERR "%s: unable to create kernel thread\n",
- __func__);
+ pr_err("%s: unable to create kernel thread\n", __func__);
return PTR_ERR(fbi->smart_thread);
}
+
return 0;
}
#else
@@ -826,7 +1256,9 @@ int pxafb_smart_flush(struct fb_info *info)
{
return 0;
}
-#endif /* CONFIG_FB_SMART_PANEL */
+
+static inline int pxafb_smart_init(struct pxafb_info *fbi) { return 0; }
+#endif /* CONFIG_FB_PXA_SMARTPANEL */
static void setup_parallel_timing(struct pxafb_info *fbi,
struct fb_var_screeninfo *var)
@@ -874,51 +1306,7 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var,
struct pxafb_info *fbi)
{
u_long flags;
- size_t nbytes;
-
-#if DEBUG_VAR
- if (!(fbi->lccr0 & LCCR0_LCDT)) {
- if (var->xres < 16 || var->xres > 1024)
- printk(KERN_ERR "%s: invalid xres %d\n",
- fbi->fb.fix.id, var->xres);
- switch (var->bits_per_pixel) {
- case 1:
- case 2:
- case 4:
- case 8:
- case 16:
- case 24:
- case 32:
- break;
- default:
- printk(KERN_ERR "%s: invalid bit depth %d\n",
- fbi->fb.fix.id, var->bits_per_pixel);
- break;
- }
- if (var->hsync_len < 1 || var->hsync_len > 64)
- printk(KERN_ERR "%s: invalid hsync_len %d\n",
- fbi->fb.fix.id, var->hsync_len);
- if (var->left_margin < 1 || var->left_margin > 255)
- printk(KERN_ERR "%s: invalid left_margin %d\n",
- fbi->fb.fix.id, var->left_margin);
- if (var->right_margin < 1 || var->right_margin > 255)
- printk(KERN_ERR "%s: invalid right_margin %d\n",
- fbi->fb.fix.id, var->right_margin);
- if (var->yres < 1 || var->yres > 1024)
- printk(KERN_ERR "%s: invalid yres %d\n",
- fbi->fb.fix.id, var->yres);
- if (var->vsync_len < 1 || var->vsync_len > 64)
- printk(KERN_ERR "%s: invalid vsync_len %d\n",
- fbi->fb.fix.id, var->vsync_len);
- if (var->upper_margin < 0 || var->upper_margin > 255)
- printk(KERN_ERR "%s: invalid upper_margin %d\n",
- fbi->fb.fix.id, var->upper_margin);
- if (var->lower_margin < 0 || var->lower_margin > 255)
- printk(KERN_ERR "%s: invalid lower_margin %d\n",
- fbi->fb.fix.id, var->lower_margin);
- }
-#endif
/* Update shadow copy atomically */
local_irq_save(flags);
@@ -929,23 +1317,13 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var,
#endif
setup_parallel_timing(fbi, var);
+ setup_base_frame(fbi, 0);
+
fbi->reg_lccr0 = fbi->lccr0 |
(LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM |
LCCR0_QDM | LCCR0_BM | LCCR0_OUM);
- fbi->reg_lccr3 |= pxafb_bpp_to_lccr3(var);
-
- nbytes = var->yres * fbi->fb.fix.line_length;
-
- if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual) {
- nbytes = nbytes / 2;
- setup_frame_dma(fbi, DMA_LOWER, PAL_NONE, nbytes, nbytes);
- }
-
- if ((var->bits_per_pixel >= 16) || (fbi->lccr0 & LCCR0_LCDT))
- setup_frame_dma(fbi, DMA_BASE, PAL_NONE, 0, nbytes);
- else
- setup_frame_dma(fbi, DMA_BASE, PAL_BASE, 0, nbytes);
+ fbi->reg_lccr3 |= pxafb_var_to_lccr3(var);
fbi->reg_lccr4 = lcd_readl(fbi, LCCR4) & ~LCCR4_PAL_FOR_MASK;
fbi->reg_lccr4 |= (fbi->lccr4 & LCCR4_PAL_FOR_MASK);
@@ -959,6 +1337,7 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var,
(lcd_readl(fbi, LCCR1) != fbi->reg_lccr1) ||
(lcd_readl(fbi, LCCR2) != fbi->reg_lccr2) ||
(lcd_readl(fbi, LCCR3) != fbi->reg_lccr3) ||
+ (lcd_readl(fbi, LCCR4) != fbi->reg_lccr4) ||
(lcd_readl(fbi, FDADR0) != fbi->fdadr[0]) ||
(lcd_readl(fbi, FDADR1) != fbi->fdadr[1]))
pxafb_schedule_work(fbi, C_REENABLE);
@@ -976,67 +1355,16 @@ static inline void __pxafb_backlight_power(struct pxafb_info *fbi, int on)
{
pr_debug("pxafb: backlight o%s\n", on ? "n" : "ff");
- if (pxafb_backlight_power)
- pxafb_backlight_power(on);
+ if (fbi->backlight_power)
+ fbi->backlight_power(on);
}
static inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on)
{
pr_debug("pxafb: LCD power o%s\n", on ? "n" : "ff");
- if (pxafb_lcd_power)
- pxafb_lcd_power(on, &fbi->fb.var);
-}
-
-static void pxafb_setup_gpio(struct pxafb_info *fbi)
-{
- int gpio, ldd_bits;
- unsigned int lccr0 = fbi->lccr0;
-
- /*
- * setup is based on type of panel supported
- */
-
- /* 4 bit interface */
- if ((lccr0 & LCCR0_CMS) == LCCR0_Mono &&
- (lccr0 & LCCR0_SDS) == LCCR0_Sngl &&
- (lccr0 & LCCR0_DPD) == LCCR0_4PixMono)
- ldd_bits = 4;
-
- /* 8 bit interface */
- else if (((lccr0 & LCCR0_CMS) == LCCR0_Mono &&
- ((lccr0 & LCCR0_SDS) == LCCR0_Dual ||
- (lccr0 & LCCR0_DPD) == LCCR0_8PixMono)) ||
- ((lccr0 & LCCR0_CMS) == LCCR0_Color &&
- (lccr0 & LCCR0_PAS) == LCCR0_Pas &&
- (lccr0 & LCCR0_SDS) == LCCR0_Sngl))
- ldd_bits = 8;
-
- /* 16 bit interface */
- else if ((lccr0 & LCCR0_CMS) == LCCR0_Color &&
- ((lccr0 & LCCR0_SDS) == LCCR0_Dual ||
- (lccr0 & LCCR0_PAS) == LCCR0_Act))
- ldd_bits = 16;
-
- else {
- printk(KERN_ERR "pxafb_setup_gpio: unable to determine "
- "bits per pixel\n");
- return;
- }
-
- for (gpio = 58; ldd_bits; gpio++, ldd_bits--)
- pxa_gpio_mode(gpio | GPIO_ALT_FN_2_OUT);
- /* 18 bit interface */
- if (fbi->fb.var.bits_per_pixel > 16) {
- pxa_gpio_mode(86 | GPIO_ALT_FN_2_OUT);
- pxa_gpio_mode(87 | GPIO_ALT_FN_2_OUT);
- }
- pxa_gpio_mode(GPIO74_LCD_FCLK_MD);
- pxa_gpio_mode(GPIO75_LCD_LCLK_MD);
- pxa_gpio_mode(GPIO76_LCD_PCLK_MD);
-
- if ((lccr0 & LCCR0_PAS) == 0)
- pxa_gpio_mode(GPIO77_LCD_ACBIAS_MD);
+ if (fbi->lcd_power)
+ fbi->lcd_power(on, &fbi->fb.var);
}
static void pxafb_enable_controller(struct pxafb_info *fbi)
@@ -1056,6 +1384,7 @@ static void pxafb_enable_controller(struct pxafb_info *fbi)
return;
/* Sequence from 11.7.10 */
+ lcd_writel(fbi, LCCR4, fbi->reg_lccr4);
lcd_writel(fbi, LCCR3, fbi->reg_lccr3);
lcd_writel(fbi, LCCR2, fbi->reg_lccr2);
lcd_writel(fbi, LCCR1, fbi->reg_lccr1);
@@ -1097,8 +1426,9 @@ static void pxafb_disable_controller(struct pxafb_info *fbi)
static irqreturn_t pxafb_handle_irq(int irq, void *dev_id)
{
struct pxafb_info *fbi = dev_id;
- unsigned int lccr0, lcsr = lcd_readl(fbi, LCSR);
+ unsigned int lccr0, lcsr, lcsr1;
+ lcsr = lcd_readl(fbi, LCSR);
if (lcsr & LCSR_LDD) {
lccr0 = lcd_readl(fbi, LCCR0);
lcd_writel(fbi, LCCR0, lccr0 | LCCR0_LDM);
@@ -1109,8 +1439,18 @@ static irqreturn_t pxafb_handle_irq(int irq, void *dev_id)
if (lcsr & LCSR_CMD_INT)
complete(&fbi->command_done);
#endif
-
lcd_writel(fbi, LCSR, lcsr);
+
+#ifdef CONFIG_FB_PXA_OVERLAY
+ lcsr1 = lcd_readl(fbi, LCSR1);
+ if (lcsr1 & LCSR1_BS(1))
+ complete(&fbi->overlay[0].branch_done);
+
+ if (lcsr1 & LCSR1_BS(2))
+ complete(&fbi->overlay[1].branch_done);
+
+ lcd_writel(fbi, LCSR1, lcsr1);
+#endif
return IRQ_HANDLED;
}
@@ -1181,7 +1521,6 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state)
if (old_state == C_ENABLE) {
__pxafb_lcd_power(fbi, 0);
pxafb_disable_controller(fbi);
- pxafb_setup_gpio(fbi);
pxafb_enable_controller(fbi);
__pxafb_lcd_power(fbi, 1);
}
@@ -1204,7 +1543,6 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state)
*/
if (old_state != C_ENABLE) {
fbi->state = C_ENABLE;
- pxafb_setup_gpio(fbi);
pxafb_enable_controller(fbi);
__pxafb_lcd_power(fbi, 1);
__pxafb_backlight_power(fbi, 1);
@@ -1303,77 +1641,34 @@ static int pxafb_resume(struct platform_device *dev)
#define pxafb_resume NULL
#endif
-/*
- * pxafb_map_video_memory():
- * Allocates the DRAM memory for the frame buffer. This buffer is
- * remapped into a non-cached, non-buffered, memory region to
- * allow palette and pixel writes to occur without flushing the
- * cache. Once this area is remapped, all virtual memory
- * access to the video memory should occur at the new region.
- */
-static int __devinit pxafb_map_video_memory(struct pxafb_info *fbi)
+static int __devinit pxafb_init_video_memory(struct pxafb_info *fbi)
{
- /*
- * We reserve one page for the palette, plus the size
- * of the framebuffer.
- */
- fbi->video_offset = PAGE_ALIGN(sizeof(struct pxafb_dma_buff));
- fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + fbi->video_offset);
- fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size,
- &fbi->map_dma, GFP_KERNEL);
-
- if (fbi->map_cpu) {
- /* prevent initial garbage on screen */
- memset(fbi->map_cpu, 0, fbi->map_size);
- fbi->fb.screen_base = fbi->map_cpu + fbi->video_offset;
- fbi->screen_dma = fbi->map_dma + fbi->video_offset;
-
- /*
- * FIXME: this is actually the wrong thing to place in
- * smem_start. But fbdev suffers from the problem that
- * it needs an API which doesn't exist (in this case,
- * dma_writecombine_mmap)
- */
- fbi->fb.fix.smem_start = fbi->screen_dma;
- fbi->palette_size = fbi->fb.var.bits_per_pixel == 8 ? 256 : 16;
-
- fbi->dma_buff = (void *) fbi->map_cpu;
- fbi->dma_buff_phys = fbi->map_dma;
- fbi->palette_cpu = (u16 *) fbi->dma_buff->palette;
+ int size = PAGE_ALIGN(fbi->video_mem_size);
- pr_debug("pxafb: palette_mem_size = 0x%08x\n", fbi->palette_size*sizeof(u16));
+ fbi->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
+ if (fbi->video_mem == NULL)
+ return -ENOMEM;
-#ifdef CONFIG_FB_PXA_SMARTPANEL
- fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff;
- fbi->n_smart_cmds = 0;
-#endif
- }
-
- return fbi->map_cpu ? 0 : -ENOMEM;
-}
+ fbi->video_mem_phys = virt_to_phys(fbi->video_mem);
+ fbi->video_mem_size = size;
-static void pxafb_decode_mode_info(struct pxafb_info *fbi,
- struct pxafb_mode_info *modes,
- unsigned int num_modes)
-{
- unsigned int i, smemlen;
-
- pxafb_setmode(&fbi->fb.var, &modes[0]);
+ fbi->fb.fix.smem_start = fbi->video_mem_phys;
+ fbi->fb.fix.smem_len = fbi->video_mem_size;
+ fbi->fb.screen_base = fbi->video_mem;
- for (i = 0; i < num_modes; i++) {
- smemlen = modes[i].xres * modes[i].yres * modes[i].bpp / 8;
- if (smemlen > fbi->fb.fix.smem_len)
- fbi->fb.fix.smem_len = smemlen;
- }
+ return fbi->video_mem ? 0 : -ENOMEM;
}
static void pxafb_decode_mach_info(struct pxafb_info *fbi,
struct pxafb_mach_info *inf)
{
unsigned int lcd_conn = inf->lcd_conn;
+ struct pxafb_mode_info *m;
+ int i;
fbi->cmap_inverse = inf->cmap_inverse;
fbi->cmap_static = inf->cmap_static;
+ fbi->lccr4 = inf->lccr4;
switch (lcd_conn & LCD_TYPE_MASK) {
case LCD_TYPE_MONO_STN:
@@ -1398,7 +1693,6 @@ static void pxafb_decode_mach_info(struct pxafb_info *fbi,
/* fall back to backward compatibility way */
fbi->lccr0 = inf->lccr0;
fbi->lccr3 = inf->lccr3;
- fbi->lccr4 = inf->lccr4;
goto decode_mode;
}
@@ -1412,7 +1706,22 @@ static void pxafb_decode_mach_info(struct pxafb_info *fbi,
fbi->lccr3 |= (lcd_conn & LCD_PCLK_EDGE_FALL) ? LCCR3_PCP : 0;
decode_mode:
- pxafb_decode_mode_info(fbi, inf->modes, inf->num_modes);
+ pxafb_setmode(&fbi->fb.var, &inf->modes[0]);
+
+ /* decide video memory size as follows:
+ * 1. default to mode of maximum resolution
+ * 2. allow platform to override
+ * 3. allow module parameter to override
+ */
+ for (i = 0, m = &inf->modes[0]; i < inf->num_modes; i++, m++)
+ fbi->video_mem_size = max_t(size_t, fbi->video_mem_size,
+ m->xres * m->yres * m->bpp / 8);
+
+ if (inf->video_mem_size > fbi->video_mem_size)
+ fbi->video_mem_size = inf->video_mem_size;
+
+ if (video_mem_size > fbi->video_mem_size)
+ fbi->video_mem_size = video_mem_size;
}
static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev)
@@ -1429,7 +1738,7 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev)
memset(fbi, 0, sizeof(struct pxafb_info));
fbi->dev = dev;
- fbi->clk = clk_get(dev, "LCDCLK");
+ fbi->clk = clk_get(dev, NULL);
if (IS_ERR(fbi->clk)) {
kfree(fbi);
return NULL;
@@ -1440,7 +1749,7 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev)
fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS;
fbi->fb.fix.type_aux = 0;
fbi->fb.fix.xpanstep = 0;
- fbi->fb.fix.ypanstep = 0;
+ fbi->fb.fix.ypanstep = 1;
fbi->fb.fix.ywrapstep = 0;
fbi->fb.fix.accel = FB_ACCEL_NONE;
@@ -1448,7 +1757,7 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev)
fbi->fb.var.activate = FB_ACTIVATE_NOW;
fbi->fb.var.height = -1;
fbi->fb.var.width = -1;
- fbi->fb.var.accel_flags = 0;
+ fbi->fb.var.accel_flags = FB_ACCELF_TEXT;
fbi->fb.var.vmode = FB_VMODE_NONINTERLACED;
fbi->fb.fbops = &pxafb_ops;
@@ -1468,10 +1777,6 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev)
INIT_WORK(&fbi->task, pxafb_task);
mutex_init(&fbi->ctrlr_lock);
init_completion(&fbi->disable_done);
-#ifdef CONFIG_FB_PXA_SMARTPANEL
- init_completion(&fbi->command_done);
- init_completion(&fbi->refresh_done);
-#endif
return fbi;
}
@@ -1544,7 +1849,9 @@ static int __devinit parse_opt(struct device *dev, char *this_opt)
s[0] = '\0';
- if (!strncmp(this_opt, "mode:", 5)) {
+ if (!strncmp(this_opt, "vmem:", 5)) {
+ video_mem_size = memparse(this_opt + 5, NULL);
+ } else if (!strncmp(this_opt, "mode:", 5)) {
return parse_opt_mode(dev, this_opt);
} else if (!strncmp(this_opt, "pixclock:", 9)) {
mode->pixclock = simple_strtoul(this_opt+9, NULL, 0);
@@ -1748,8 +2055,7 @@ static int __devinit pxafb_probe(struct platform_device *dev)
ret = -EINVAL;
goto failed;
}
- pxafb_backlight_power = inf->pxafb_backlight_power;
- pxafb_lcd_power = inf->pxafb_lcd_power;
+
fbi = pxafb_init_fbinfo(&dev->dev);
if (!fbi) {
/* only reason for pxafb_init_fbinfo to fail is kmalloc */
@@ -1758,6 +2064,9 @@ static int __devinit pxafb_probe(struct platform_device *dev)
goto failed;
}
+ fbi->backlight_power = inf->pxafb_backlight_power;
+ fbi->lcd_power = inf->pxafb_lcd_power;
+
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (r == NULL) {
dev_err(&dev->dev, "no I/O memory resource defined\n");
@@ -1779,12 +2088,20 @@ static int __devinit pxafb_probe(struct platform_device *dev)
goto failed_free_res;
}
- /* Initialize video memory */
- ret = pxafb_map_video_memory(fbi);
+ fbi->dma_buff_size = PAGE_ALIGN(sizeof(struct pxafb_dma_buff));
+ fbi->dma_buff = dma_alloc_coherent(fbi->dev, fbi->dma_buff_size,
+ &fbi->dma_buff_phys, GFP_KERNEL);
+ if (fbi->dma_buff == NULL) {
+ dev_err(&dev->dev, "failed to allocate memory for DMA\n");
+ ret = -ENOMEM;
+ goto failed_free_io;
+ }
+
+ ret = pxafb_init_video_memory(fbi);
if (ret) {
dev_err(&dev->dev, "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
- goto failed_free_io;
+ goto failed_free_dma;
}
irq = platform_get_irq(dev, 0);
@@ -1801,13 +2118,12 @@ static int __devinit pxafb_probe(struct platform_device *dev)
goto failed_free_mem;
}
-#ifdef CONFIG_FB_PXA_SMARTPANEL
ret = pxafb_smart_init(fbi);
if (ret) {
dev_err(&dev->dev, "failed to initialize smartpanel\n");
goto failed_free_irq;
}
-#endif
+
/*
* This makes sure that our colour bitfield
* descriptors are correctly initialised.
@@ -1833,6 +2149,8 @@ static int __devinit pxafb_probe(struct platform_device *dev)
goto failed_free_cmap;
}
+ pxafb_overlay_init(fbi);
+
#ifdef CONFIG_CPU_FREQ
fbi->freq_transition.notifier_call = pxafb_freq_transition;
fbi->freq_policy.notifier_call = pxafb_freq_policy;
@@ -1855,8 +2173,10 @@ failed_free_cmap:
failed_free_irq:
free_irq(irq, fbi);
failed_free_mem:
- dma_free_writecombine(&dev->dev, fbi->map_size,
- fbi->map_cpu, fbi->map_dma);
+ free_pages_exact(fbi->video_mem, fbi->video_mem_size);
+failed_free_dma:
+ dma_free_coherent(&dev->dev, fbi->dma_buff_size,
+ fbi->dma_buff, fbi->dma_buff_phys);
failed_free_io:
iounmap(fbi->mmio_base);
failed_free_res:
@@ -1881,6 +2201,7 @@ static int __devexit pxafb_remove(struct platform_device *dev)
info = &fbi->fb;
+ pxafb_overlay_exit(fbi);
unregister_framebuffer(info);
pxafb_disable_controller(fbi);
@@ -1891,8 +2212,10 @@ static int __devexit pxafb_remove(struct platform_device *dev)
irq = platform_get_irq(dev, 0);
free_irq(irq, fbi);
- dma_free_writecombine(&dev->dev, fbi->map_size,
- fbi->map_cpu, fbi->map_dma);
+ free_pages_exact(fbi->video_mem, fbi->video_mem_size);
+
+ dma_free_writecombine(&dev->dev, fbi->dma_buff_size,
+ fbi->dma_buff, fbi->dma_buff_phys);
iounmap(fbi->mmio_base);
diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h
index 31541b86f13d..2353521c5c8c 100644
--- a/drivers/video/pxafb.h
+++ b/drivers/video/pxafb.h
@@ -54,11 +54,55 @@ enum {
#define PALETTE_SIZE (256 * 4)
#define CMD_BUFF_SIZE (1024 * 50)
+/* NOTE: the palette and frame dma descriptors are doubled to allow
+ * the 2nd set for branch settings (FBRx)
+ */
struct pxafb_dma_buff {
unsigned char palette[PAL_MAX * PALETTE_SIZE];
uint16_t cmd_buff[CMD_BUFF_SIZE];
- struct pxafb_dma_descriptor pal_desc[PAL_MAX];
- struct pxafb_dma_descriptor dma_desc[DMA_MAX];
+ struct pxafb_dma_descriptor pal_desc[PAL_MAX * 2];
+ struct pxafb_dma_descriptor dma_desc[DMA_MAX * 2];
+};
+
+enum {
+ OVERLAY1,
+ OVERLAY2,
+};
+
+enum {
+ OVERLAY_FORMAT_RGB = 0,
+ OVERLAY_FORMAT_YUV444_PACKED,
+ OVERLAY_FORMAT_YUV444_PLANAR,
+ OVERLAY_FORMAT_YUV422_PLANAR,
+ OVERLAY_FORMAT_YUV420_PLANAR,
+};
+
+#define NONSTD_TO_XPOS(x) (((x) >> 0) & 0x3ff)
+#define NONSTD_TO_YPOS(x) (((x) >> 10) & 0x3ff)
+#define NONSTD_TO_PFOR(x) (((x) >> 20) & 0x7)
+
+struct pxafb_layer;
+
+struct pxafb_layer_ops {
+ void (*enable)(struct pxafb_layer *);
+ void (*disable)(struct pxafb_layer *);
+ void (*setup)(struct pxafb_layer *);
+};
+
+struct pxafb_layer {
+ struct fb_info fb;
+ int id;
+ atomic_t usage;
+ uint32_t control[2];
+
+ struct pxafb_layer_ops *ops;
+
+ void __iomem *video_mem;
+ unsigned long video_mem_phys;
+ size_t video_mem_size;
+ struct completion branch_done;
+
+ struct pxafb_info *fbi;
};
struct pxafb_info {
@@ -69,24 +113,15 @@ struct pxafb_info {
void __iomem *mmio_base;
struct pxafb_dma_buff *dma_buff;
+ size_t dma_buff_size;
dma_addr_t dma_buff_phys;
- dma_addr_t fdadr[DMA_MAX];
-
- /*
- * These are the addresses we mapped
- * the framebuffer memory region to.
- */
- /* raw memory addresses */
- dma_addr_t map_dma; /* physical */
- u_char * map_cpu; /* virtual */
- u_int map_size;
-
- /* addresses of pieces placed in raw buffer */
- u_char * screen_cpu; /* virtual address of frame buffer */
- dma_addr_t screen_dma; /* physical address of frame buffer */
+ dma_addr_t fdadr[DMA_MAX * 2];
+
+ void __iomem *video_mem; /* virtual address of frame buffer */
+ unsigned long video_mem_phys; /* physical address of frame buffer */
+ size_t video_mem_size; /* size of the frame buffer */
u16 * palette_cpu; /* virtual address of palette memory */
u_int palette_size;
- ssize_t video_offset;
u_int lccr0;
u_int lccr3;
@@ -120,10 +155,17 @@ struct pxafb_info {
struct task_struct *smart_thread;
#endif
+#ifdef CONFIG_FB_PXA_OVERLAY
+ struct pxafb_layer overlay[2];
+#endif
+
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
struct notifier_block freq_policy;
#endif
+
+ void (*lcd_power)(int, struct fb_var_screeninfo *);
+ void (*backlight_power)(int);
};
#define TO_INF(ptr,member) container_of(ptr,struct pxafb_info,member)
@@ -148,4 +190,10 @@ struct pxafb_info {
#define MIN_XRES 64
#define MIN_YRES 64
+/* maximum X and Y resolutions - note these are limits from the register
+ * bits length instead of the real ones
+ */
+#define MAX_XRES 1024
+#define MAX_YRES 1024
+
#endif /* __PXAFB_H__ */
diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c
index c052bd4c0b06..076f946fa0f5 100644
--- a/drivers/video/sa1100fb.c
+++ b/drivers/video/sa1100fb.c
@@ -114,7 +114,7 @@
* - convert dma address types to dma_addr_t
* - remove unused 'montype' stuff
* - remove redundant zero inits of init_var after the initial
- * memzero.
+ * memset.
* - remove allow_modeset (acornfb idea does not belong here)
*
* 2001/05/28: <rmk@arm.linux.org.uk>
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index 5b78fd0aff0a..018c070a357f 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -176,7 +176,7 @@ int register_virtio_device(struct virtio_device *dev)
/* Assign a unique device index and hence name. */
dev->index = dev_index++;
- sprintf(dev->dev.bus_id, "virtio%u", dev->index);
+ dev_set_name(&dev->dev, "virtio%u", dev->index);
/* We always start by resetting the device, in case a previous
* driver messed it up. This also tests that code path a little. */
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 62eab43152d2..59268266b79a 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -56,6 +56,15 @@ static struct virtio_device_id id_table[] = {
{ 0 },
};
+static u32 page_to_balloon_pfn(struct page *page)
+{
+ unsigned long pfn = page_to_pfn(page);
+
+ BUILD_BUG_ON(PAGE_SHIFT < VIRTIO_BALLOON_PFN_SHIFT);
+ /* Convert pfn from Linux page size to balloon page size. */
+ return pfn >> (PAGE_SHIFT - VIRTIO_BALLOON_PFN_SHIFT);
+}
+
static void balloon_ack(struct virtqueue *vq)
{
struct virtio_balloon *vb;
@@ -99,7 +108,7 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num)
msleep(200);
break;
}
- vb->pfns[vb->num_pfns] = page_to_pfn(page);
+ vb->pfns[vb->num_pfns] = page_to_balloon_pfn(page);
totalram_pages--;
vb->num_pages++;
list_add(&page->lru, &vb->pages);
@@ -132,7 +141,7 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num)
for (vb->num_pfns = 0; vb->num_pfns < num; vb->num_pfns++) {
page = list_first_entry(&vb->pages, struct page, lru);
list_del(&page->lru);
- vb->pfns[vb->num_pfns] = page_to_pfn(page);
+ vb->pfns[vb->num_pfns] = page_to_balloon_pfn(page);
vb->num_pages--;
}
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index c7dc37c7cce9..265fdf2d1276 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -75,7 +75,7 @@ MODULE_DEVICE_TABLE(pci, virtio_pci_id_table);
* would make more sense for virtio to not insist on having it's own device. */
static struct device virtio_pci_root = {
.parent = NULL,
- .bus_id = "virtio-pci",
+ .init_name = "virtio-pci",
};
/* Convert a generic virtio device to our structure */
@@ -216,7 +216,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
struct virtio_pci_vq_info *info;
struct virtqueue *vq;
- unsigned long flags;
+ unsigned long flags, size;
u16 num;
int err;
@@ -237,19 +237,20 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
info->queue_index = index;
info->num = num;
- info->queue = kzalloc(PAGE_ALIGN(vring_size(num,PAGE_SIZE)), GFP_KERNEL);
+ size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN));
+ info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO);
if (info->queue == NULL) {
err = -ENOMEM;
goto out_info;
}
/* activate the queue */
- iowrite32(virt_to_phys(info->queue) >> PAGE_SHIFT,
+ iowrite32(virt_to_phys(info->queue) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
/* create the vring */
- vq = vring_new_virtqueue(info->num, vdev, info->queue,
- vp_notify, callback);
+ vq = vring_new_virtqueue(info->num, VIRTIO_PCI_VRING_ALIGN,
+ vdev, info->queue, vp_notify, callback);
if (!vq) {
err = -ENOMEM;
goto out_activate_queue;
@@ -266,7 +267,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
out_activate_queue:
iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
- kfree(info->queue);
+ free_pages_exact(info->queue, size);
out_info:
kfree(info);
return ERR_PTR(err);
@@ -277,7 +278,7 @@ static void vp_del_vq(struct virtqueue *vq)
{
struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
struct virtio_pci_vq_info *info = vq->priv;
- unsigned long flags;
+ unsigned long flags, size;
spin_lock_irqsave(&vp_dev->lock, flags);
list_del(&info->node);
@@ -289,7 +290,8 @@ static void vp_del_vq(struct virtqueue *vq)
iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
- kfree(info->queue);
+ size = PAGE_ALIGN(vring_size(info->num, VIRTIO_PCI_VRING_ALIGN));
+ free_pages_exact(info->queue, size);
kfree(info);
}
@@ -305,6 +307,20 @@ static struct virtio_config_ops virtio_pci_config_ops = {
.finalize_features = vp_finalize_features,
};
+static void virtio_pci_release_dev(struct device *_d)
+{
+ struct virtio_device *dev = container_of(_d, struct virtio_device, dev);
+ struct virtio_pci_device *vp_dev = to_vp_device(dev);
+ struct pci_dev *pci_dev = vp_dev->pci_dev;
+
+ free_irq(pci_dev->irq, vp_dev);
+ pci_set_drvdata(pci_dev, NULL);
+ pci_iounmap(pci_dev, vp_dev->ioaddr);
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
+ kfree(vp_dev);
+}
+
/* the PCI probing function */
static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *id)
@@ -328,6 +344,7 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
return -ENOMEM;
vp_dev->vdev.dev.parent = &virtio_pci_root;
+ vp_dev->vdev.dev.release = virtio_pci_release_dev;
vp_dev->vdev.config = &virtio_pci_config_ops;
vp_dev->pci_dev = pci_dev;
INIT_LIST_HEAD(&vp_dev->virtqueues);
@@ -357,7 +374,7 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
/* register a handler for the queue with the PCI device's interrupt */
err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED,
- vp_dev->vdev.dev.bus_id, vp_dev);
+ dev_name(&vp_dev->vdev.dev), vp_dev);
if (err)
goto out_set_drvdata;
@@ -387,12 +404,6 @@ static void __devexit virtio_pci_remove(struct pci_dev *pci_dev)
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
unregister_virtio_device(&vp_dev->vdev);
- free_irq(pci_dev->irq, vp_dev);
- pci_set_drvdata(pci_dev, NULL);
- pci_iounmap(pci_dev, vp_dev->ioaddr);
- pci_release_regions(pci_dev);
- pci_disable_device(pci_dev);
- kfree(vp_dev);
}
#ifdef CONFIG_PM
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 6eb5303fed11..5777196bf6c9 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -274,6 +274,7 @@ static struct virtqueue_ops vring_vq_ops = {
};
struct virtqueue *vring_new_virtqueue(unsigned int num,
+ unsigned int vring_align,
struct virtio_device *vdev,
void *pages,
void (*notify)(struct virtqueue *),
@@ -292,7 +293,7 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,
if (!vq)
return NULL;
- vring_init(&vq->vring, num, pages, PAGE_SIZE);
+ vring_init(&vq->vring, num, pages, vring_align);
vq->vq.callback = callback;
vq->vq.vdev = vdev;
vq->vq.vq_ops = &vring_vq_ops;
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index f7f6ce82a5e2..e31925ee8346 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -42,7 +42,7 @@
#undef S3C_VA_WATCHDOG
#define S3C_VA_WATCHDOG (0)
-#include <asm/plat-s3c/regs-watchdog.h>
+#include <plat/regs-watchdog.h>
#define PFX "s3c2410-wdt: "
diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c
index ed01e4c2beff..e19b45794717 100644
--- a/drivers/watchdog/sa1100_wdt.c
+++ b/drivers/watchdog/sa1100_wdt.c
@@ -27,6 +27,7 @@
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
+#include <linux/timex.h>
#ifdef CONFIG_ARCH_PXA
#include <mach/pxa-regs.h>
@@ -35,8 +36,7 @@
#include <mach/reset.h>
#include <mach/hardware.h>
-#define OSCR_FREQ CLOCK_TICK_RATE
-
+static unsigned long oscr_freq;
static unsigned long sa1100wdt_users;
static int pre_margin;
static int boot_status;
@@ -123,12 +123,12 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd,
break;
}
- pre_margin = OSCR_FREQ * time;
+ pre_margin = oscr_freq * time;
OSMR3 = OSCR + pre_margin;
/*fall through*/
case WDIOC_GETTIMEOUT:
- ret = put_user(pre_margin / OSCR_FREQ, p);
+ ret = put_user(pre_margin / oscr_freq, p);
break;
}
return ret;
@@ -155,6 +155,8 @@ static int __init sa1100dog_init(void)
{
int ret;
+ oscr_freq = get_clock_tick_rate();
+
/*
* Read the reset status, and save it for later. If
* we suspend, RCSR will be cleared, and the watchdog
@@ -162,7 +164,7 @@ static int __init sa1100dog_init(void)
*/
boot_status = (reset_status & RESET_STATUS_WATCHDOG) ?
WDIOF_CARDRESET : 0;
- pre_margin = OSCR_FREQ * margin;
+ pre_margin = oscr_freq * margin;
ret = misc_register(&sa1100dog_miscdev);
if (ret == 0)
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index 1e3b934a4cf7..46625cd38743 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -141,8 +141,12 @@ static void init_evtchn_cpu_bindings(void)
int i;
/* By default all event channels notify CPU#0. */
- for_each_irq_desc(i, desc)
+ for_each_irq_desc(i, desc) {
+ if (!desc)
+ continue;
+
desc->affinity = cpumask_of_cpu(0);
+ }
#endif
memset(cpu_evtchn, 0, sizeof(cpu_evtchn));
@@ -229,15 +233,20 @@ static void unmask_evtchn(int port)
static int find_unbound_irq(void)
{
int irq;
+ struct irq_desc *desc;
/* Only allocate from dynirq range */
- for_each_irq_nr(irq)
+ for (irq = 0; irq < nr_irqs; irq++)
if (irq_bindcount[irq] == 0)
break;
if (irq == nr_irqs)
panic("No available IRQ to bind to: increase nr_irqs!\n");
+ desc = irq_to_desc_alloc_cpu(irq, 0);
+ if (WARN_ON(desc == NULL))
+ return -1;
+
return irq;
}
@@ -792,7 +801,7 @@ void xen_irq_resume(void)
mask_evtchn(evtchn);
/* No IRQ <-> event-channel mappings. */
- for_each_irq_nr(irq)
+ for (irq = 0; irq < nr_irqs; irq++)
irq_info[irq].evtchn = 0; /* zap event-channel binding */
for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++)
@@ -824,7 +833,7 @@ void __init xen_init_IRQ(void)
mask_evtchn(i);
/* Dynamic IRQ space is currently unbound. Zero the refcnts. */
- for_each_irq_nr(i)
+ for (i = 0; i < nr_irqs; i++)
irq_bindcount[i] = 0;
irq_ctx_init(smp_processor_id());